diff --git a/Cargo.lock b/Cargo.lock index c308da5..0ba121b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,16 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "addr2line" version = "0.25.1" @@ -176,7 +166,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -187,7 +177,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -397,7 +387,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_urlencoded", - "thiserror 2.0.17", + "thiserror", "tokio", "tokio-stream", "tokio-util", @@ -413,8 +403,8 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85a885520bf6249ab931a764ffdb87b0ceef48e6e7d807cfdb21b751e086e1ad" dependencies = [ - "prost 0.14.1", - "prost-types 0.14.1", + "prost", + "prost-types", "tonic", "tonic-prost", "ureq", @@ -430,7 +420,7 @@ dependencies = [ "bollard-buildkit-proto", "bytes", "chrono", - "prost 0.14.1", + "prost", "serde", "serde_json", "serde_repr", @@ -459,7 +449,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.111", ] [[package]] @@ -674,7 +664,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -698,7 +688,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.111", ] [[package]] @@ -709,7 +699,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -762,7 +752,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -792,7 +782,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -836,7 +826,7 @@ dependencies = [ "dsl_auto_type", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -856,7 +846,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe2444076b48641147115697648dc743c2c00b61adade0f01ce67133c7babe8c" dependencies = [ - "syn", + "syn 2.0.111", ] [[package]] @@ -879,7 +869,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -896,7 +886,7 @@ checksum = "a3e58aa4b749bede53defb647ad26bdbca2580a2a35c8cc3fed8483911ae52f8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -927,7 +917,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1009,18 +999,6 @@ dependencies = [ "log", ] -[[package]] -name = "enum_dispatch" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "env_filter" version = "0.1.4" @@ -1245,7 +1223,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1837,7 +1815,7 @@ checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1986,37 +1964,13 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" -[[package]] -name = "logos" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7251356ef8cb7aec833ddf598c6cb24d17b689d20b993f9d11a3d764e34e6458" -dependencies = [ - "logos-derive 0.14.4", -] - [[package]] name = "logos" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff472f899b4ec2d99161c51f60ff7075eeb3097069a36050d8037a6325eb8154" dependencies = [ - "logos-derive 0.15.1", -] - -[[package]] -name = "logos-codegen" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f80069600c0d66734f5ff52cc42f2dabd6b29d205f333d61fd7832e9e9963f" -dependencies = [ - "beef", - "fnv", - "lazy_static", - "proc-macro2", - "quote", - "regex-syntax", - "syn", + "logos-derive", ] [[package]] @@ -2032,16 +1986,7 @@ dependencies = [ "quote", "regex-syntax", "rustc_version 0.4.1", - "syn", -] - -[[package]] -name = "logos-derive" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24fb722b06a9dc12adb0963ed585f19fc61dc5413e6a9be9422ef92c091e731d" -dependencies = [ - "logos-codegen 0.14.4", + "syn 2.0.111", ] [[package]] @@ -2050,7 +1995,7 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "605d9697bcd5ef3a42d38efc51541aa3d6a4a25f7ab6d1ed0da5ac632a26b470" dependencies = [ - "logos-codegen 0.15.1", + "logos-codegen", ] [[package]] @@ -2097,40 +2042,59 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "miden-agglayer" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a867217bab689c0539f6b4797cb452f0932de6904479a38f1322e045b9383b" +dependencies = [ + "fs-err", + "miden-assembly", + "miden-core", + "miden-core-lib", + "miden-protocol", + "miden-standards", + "miden-utils-sync", + "regex", + "walkdir", +] + [[package]] name = "miden-air" -version = "0.19.1" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06acfd2ddc25b68f9d23d2add3f15c0ec3f9890ce6418409d71bea9dc6590bd0" +checksum = "5cca9632323bd4e32ae5b21b101ed417a646f5d72196b1bf3f1ca889a148322a" dependencies = [ "miden-core", "miden-utils-indexing", - "thiserror 2.0.17", + "thiserror", "winter-air", "winter-prover", ] [[package]] name = "miden-assembly" -version = "0.19.1" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1219b9e48bb286b58a23bb65cf74baa1b24ddbcb462ca625b38186674571047" +checksum = "2395b2917aea613a285d3425d1ca07e6c45442e2b34febdea2081db555df62fc" dependencies = [ + "env_logger", "log", "miden-assembly-syntax", "miden-core", "miden-mast-package", "smallvec", - "thiserror 2.0.17", + "thiserror", ] [[package]] name = "miden-assembly-syntax" -version = "0.19.1" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eeaef2853061c54527bb2664c0c832ce3d1f80847c79512455fec3b93057f2a" +checksum = "1f9bed037d137f209b9e7b28811ec78c0536b3f9259d6f4ceb5823c87513b346" dependencies = [ "aho-corasick", + "env_logger", "lalrpop", "lalrpop-util", "log", @@ -2139,29 +2103,29 @@ dependencies = [ "miden-utils-diagnostics", "midenc-hir-type", "proptest", + "proptest-derive", "regex", "rustc_version 0.4.1", "semver 1.0.27", "smallvec", - "thiserror 2.0.17", + "thiserror", ] [[package]] name = "miden-block-prover" -version = "0.12.4" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec766587e838664ded55fa926d0611244cac2fe23b7cec202d8db0a85d9e536e" +checksum = "6e92a0ddae8d0983e37bc636edba741947b1e3dc63baed2ad85921342080154a" dependencies = [ - "miden-lib", - "miden-objects", - "thiserror 2.0.17", + "miden-protocol", + "thiserror", ] [[package]] name = "miden-client" -version = "0.12.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c061c44b345267a9d144fb1cb2d2f7b47a9257f52d7907004297df8a3de1d7" +checksum = "68b256399e6f0d7ae53a592b771a1286c46ca6b45397d1c5fda6d83dbd222357" dependencies = [ "anyhow", "async-trait", @@ -2169,22 +2133,20 @@ dependencies = [ "futures", "getrandom 0.3.4", "hex", - "miden-lib", + "miden-mast-package", "miden-node-proto-build", "miden-note-transport-proto-build", - "miden-objects", + "miden-protocol", "miden-remote-prover-client", + "miden-standards", "miden-testing", "miden-tx", "miette", - "prost 0.14.1", - "prost-build", - "prost-types 0.14.1", - "protox 0.7.2", + "prost", + "prost-types", "rand", - "thiserror 2.0.17", + "thiserror", "tonic", - "tonic-build", "tonic-health", "tonic-prost", "tonic-prost-build", @@ -2196,9 +2158,9 @@ dependencies = [ [[package]] name = "miden-client-sqlite-store" -version = "0.12.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57f806b34687798a0545177619196017362cb3b67bd0c7003081d1fd5d179c70" +checksum = "bb5de50937147fafbc40dd25732e15b1a410ef938d8417294ea7fa3bad900f4a" dependencies = [ "anyhow", "async-trait", @@ -2206,40 +2168,62 @@ dependencies = [ "deadpool", "deadpool-sync", "miden-client", - "miden-objects", + "miden-protocol", "rusqlite", "rusqlite_migration", - "thiserror 2.0.17", + "thiserror", "tokio", ] [[package]] name = "miden-core" -version = "0.19.1" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "452a00429d05c416001ec0578291eb88e115cf94fc22b3308267abfdcd813440" +checksum = "8714aa5f86c59e647b7417126b32adc4ef618f835964464f5425549df76b6d03" dependencies = [ - "enum_dispatch", + "derive_more", + "itertools", "miden-crypto", "miden-debug-types", "miden-formatting", + "miden-utils-core-derive", "miden-utils-indexing", "num-derive", "num-traits", - "thiserror 2.0.17", + "proptest", + "proptest-derive", + "thiserror", "winter-math", "winter-utils", ] +[[package]] +name = "miden-core-lib" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb16a4d39202c59a7964d3585cd5af21a46a759ff6452cb5f20723ed5af4362" +dependencies = [ + "env_logger", + "fs-err", + "miden-assembly", + "miden-core", + "miden-crypto", + "miden-processor", + "miden-utils-sync", + "sha2", + "thiserror", +] + [[package]] name = "miden-crypto" -version = "0.18.4" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0048d2d987f215bc9633ced499a8c488d0e2474350c765f904b87cae3462acb7" +checksum = "6e28b6e110f339c2edc2760a8cb94863f0a055ee658a49bc90c8560eff2feef4" dependencies = [ "blake3", "cc", "chacha20poly1305", + "curve25519-dalek", "ed25519-dalek", "flume", "glob", @@ -2254,9 +2238,10 @@ dependencies = [ "rand_core 0.9.3", "rand_hc", "rayon", + "sha2", "sha3", "subtle", - "thiserror 2.0.17", + "thiserror", "winter-crypto", "winter-math", "winter-utils", @@ -2265,19 +2250,19 @@ dependencies = [ [[package]] name = "miden-crypto-derive" -version = "0.18.4" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3b38aace84e157fb02aba8f8ae85bbf8c3afdcdbdf8190fbe7476f3be7ef44" +checksum = "f40e95b9c7c99ed6bbf073d9e02721d812dedd2c195019c0a0e0a3dbb9cbf034" dependencies = [ "quote", - "syn", + "syn 2.0.111", ] [[package]] name = "miden-debug-types" -version = "0.19.1" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97eed62ac0ca7420e49148fd306c74786b23a8d31df6da6277c671ba3e5c619a" +checksum = "cd1494f102ad5b9fa43e391d2601186dc601f41ab7dcd8a23ecca9bf3ef930f4" dependencies = [ "memchr", "miden-crypto", @@ -2288,7 +2273,7 @@ dependencies = [ "paste", "serde", "serde_spanned", - "thiserror 2.0.17", + "thiserror", ] [[package]] @@ -2300,35 +2285,16 @@ dependencies = [ "unicode-width 0.1.14", ] -[[package]] -name = "miden-lib" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598582071e5b0ec835d06288857d4ddc0090a98bd4c17e408fa56b2c43f45d73" -dependencies = [ - "Inflector", - "fs-err", - "miden-assembly", - "miden-core", - "miden-objects", - "miden-processor", - "miden-stdlib", - "rand", - "regex", - "thiserror 2.0.17", - "walkdir", -] - [[package]] name = "miden-mast-package" -version = "0.19.1" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d13e6ba2b357551598f13396ed52f8f21aa99979aa3b338bb5521feeda19c8a" +checksum = "692185bfbe0ecdb28bf623f1f8c88282cd6727ba081a28e23b301bdde1b45be4" dependencies = [ "derive_more", "miden-assembly-syntax", "miden-core", - "thiserror 2.0.17", + "thiserror", ] [[package]] @@ -2354,10 +2320,10 @@ dependencies = [ "supports-color", "supports-hyperlinks", "supports-unicode", - "syn", + "syn 2.0.111", "terminal_size 0.3.0", "textwrap", - "thiserror 2.0.17", + "thiserror", "trybuild", "unicode-width 0.1.14", ] @@ -2370,7 +2336,7 @@ checksum = "86a905f3ea65634dd4d1041a4f0fd0a3e77aa4118341d265af1a94339182222f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2380,7 +2346,7 @@ dependencies = [ "miden-client", "miden-multisig-test-utils", "rand", - "thiserror 2.0.17", + "thiserror", "tokio", ] @@ -2417,7 +2383,7 @@ dependencies = [ "tempfile", "testcontainers", "testcontainers-modules", - "thiserror 2.0.17", + "thiserror", "tokio", "tracing", "url", @@ -2443,7 +2409,7 @@ dependencies = [ "pq-sys", "serde", "serde_with", - "thiserror 2.0.17", + "thiserror", "tokio", "tower-http", "tracing", @@ -2466,7 +2432,7 @@ dependencies = [ "oblux", "rustls", "rustls-native-certs", - "thiserror 2.0.17", + "thiserror", "tokio", "tokio-postgres", "tokio-postgres-rustls", @@ -2486,79 +2452,95 @@ dependencies = [ [[package]] name = "miden-node-proto-build" -version = "0.12.5" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8059a6beaabf87cc58c24a9c51d01fbd6d9b46edc2522125442962ce279ec2" +checksum = "c57204342fbca380d96d1f08c0fe7bb893a936fbe98a280c1bf798531fc7e319" dependencies = [ "fs-err", "miette", - "protox 0.9.0", + "protox", "tonic-prost-build", ] [[package]] name = "miden-note-transport-proto-build" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a7b3a64c71d33f771d32cde58559207819a64ada9add0acb31857e111b9d" +checksum = "ec23738a2eee393524a849a8dce982ad824050cc54abde145d4fb62b92c84198" dependencies = [ "fs-err", "miette", - "protox 0.9.0", + "protox", "tonic-prost-build", ] [[package]] -name = "miden-objects" -version = "0.12.4" +name = "miden-processor" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace4018bb2d6cdbcff4d86d8af5ade8efca9f0479f7e5775c7f09cfab5f91ebe" +checksum = "0e09f7916b1e7505f74a50985a185fdea4c0ceb8f854a34c90db28e3f7da7ab6" +dependencies = [ + "itertools", + "miden-air", + "miden-core", + "miden-debug-types", + "miden-utils-diagnostics", + "miden-utils-indexing", + "paste", + "rayon", + "thiserror", + "tokio", + "tracing", + "winter-prover", +] + +[[package]] +name = "miden-protocol" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "785be319a826c9cb43d2e1a41a1fb1eee3f2baafe360e0d743690641f7c93ad5" dependencies = [ "bech32", + "fs-err", "getrandom 0.3.4", "miden-assembly", "miden-assembly-syntax", "miden-core", + "miden-core-lib", "miden-crypto", "miden-mast-package", "miden-processor", - "miden-stdlib", + "miden-protocol-macros", "miden-utils-sync", "miden-verifier", "rand", + "rand_chacha", "rand_xoshiro", + "regex", "semver 1.0.27", "serde", - "thiserror 2.0.17", + "thiserror", "toml", + "walkdir", "winter-rand-utils", ] [[package]] -name = "miden-processor" -version = "0.19.1" +name = "miden-protocol-macros" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ef77929651b8755965cde8f589bd38e2345a619d54cab6427f91aa23c47f6a" +checksum = "f2dc854c1b9e49e82d3f39c5710345226e0b2a62ec0ea220c616f1f3a099cfb3" dependencies = [ - "itertools", - "miden-air", - "miden-core", - "miden-debug-types", - "miden-utils-diagnostics", - "miden-utils-indexing", - "paste", - "rayon", - "thiserror 2.0.17", - "tokio", - "tracing", - "winter-prover", + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] name = "miden-prover" -version = "0.19.1" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c30a5d10baeec17b9336de8544cb7f9b96b32de757c4cfb8d95ee0521bb5cd" +checksum = "d45e30526be72b8af0fd1d8b24c9cba8ac1187ca335dcee38b8e5e20234e7698" dependencies = [ "miden-air", "miden-debug-types", @@ -2570,17 +2552,18 @@ dependencies = [ [[package]] name = "miden-remote-prover-client" -version = "0.12.5" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b319ea63a16a95c04ed16b1626fb9eae581acc79c46050ba231e4d8cb9b06aae" +checksum = "f9c5a1ccd22a8638c276e8dbffcb527cf1eeb14c536860de46f948bc1ff8449e" dependencies = [ + "fs-err", "getrandom 0.3.4", "miden-node-proto-build", - "miden-objects", + "miden-protocol", "miden-tx", "miette", - "prost 0.14.1", - "thiserror 2.0.17", + "prost", + "thiserror", "tokio", "tonic", "tonic-prost", @@ -2589,72 +2572,85 @@ dependencies = [ ] [[package]] -name = "miden-stdlib" -version = "0.19.1" +name = "miden-standards" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e90a5de45a1e6213ff17b66fff8accde0bbc64264e2c22bbcb9a895f8f3b767" +checksum = "98e33771fc35e1e640582bcd26c88b2ab449dd3a70888b315546d0d3447f4bb3" dependencies = [ - "env_logger", "fs-err", "miden-assembly", "miden-core", - "miden-crypto", + "miden-core-lib", "miden-processor", - "miden-utils-sync", - "thiserror 2.0.17", + "miden-protocol", + "rand", + "regex", + "thiserror", + "walkdir", ] [[package]] name = "miden-testing" -version = "0.12.4" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda0d572d7415682ed168f616becf006825aa04b89692f9907cbb3e3586bf46a" +checksum = "ae5d41a888d1a5e520a9312a170975d0fbadefb1b9200543cebdf54dd0960310" dependencies = [ "anyhow", "itertools", + "miden-agglayer", + "miden-assembly", "miden-block-prover", - "miden-lib", - "miden-objects", + "miden-core-lib", "miden-processor", + "miden-protocol", + "miden-standards", "miden-tx", "miden-tx-batch-prover", "rand", "rand_chacha", - "thiserror 2.0.17", "winterfell", ] [[package]] name = "miden-tx" -version = "0.12.4" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d959064f99ce09fc38e9b6b4dc24c3fa80a63072bf5840a1074ca4ed5e9c911" +checksum = "430e4ee02b5efb71b104926e229441e0071a93a259a70740bf8c436495caa64f" dependencies = [ - "miden-lib", - "miden-objects", "miden-processor", + "miden-protocol", "miden-prover", + "miden-standards", "miden-verifier", - "rand", - "thiserror 2.0.17", - "tokio", + "thiserror", ] [[package]] name = "miden-tx-batch-prover" -version = "0.12.4" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5029810b106654a1ec5d7d7123945db91b96bc4f4187715d0c2cfe0b0a53af4" +checksum = "03bc209b6487ebac0de230461e229a99d17ed73596c7d99fc59eea47a28a89cc" dependencies = [ - "miden-objects", + "miden-protocol", "miden-tx", ] +[[package]] +name = "miden-utils-core-derive" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b1d490e6d7b509622d3c2cc69ffd66ad48bf953dc614579b568fe956ce0a6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "miden-utils-diagnostics" -version = "0.19.1" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3ff4c019d96539a7066626efb4dce5c9fb7b0e44e961b0c2571e78f34236d5" +checksum = "52658f6dc091c1c78e8b35ee3e7ff3dad53051971a3c514e461f581333758fe7" dependencies = [ "miden-crypto", "miden-debug-types", @@ -2665,18 +2661,18 @@ dependencies = [ [[package]] name = "miden-utils-indexing" -version = "0.19.1" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c798250bee4e856d4f18c161e91cdcbef1906f6614d00cf0063b47031c0f8cc6" +checksum = "eeff7bcb7875b222424bdfb657a7cf21a55e036aa7558ebe1f5d2e413b440d0d" dependencies = [ - "thiserror 2.0.17", + "thiserror", ] [[package]] name = "miden-utils-sync" -version = "0.19.1" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feebe7d896c013ea74dbc98de978836606356a044d4ed3b61ded54d3b319d89f" +checksum = "41d53d1ab5b275d8052ad9c4121071cb184bc276ee74354b0d8a2075e5c1d1f0" dependencies = [ "lock_api", "loom", @@ -2685,13 +2681,13 @@ dependencies = [ [[package]] name = "miden-verifier" -version = "0.19.1" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f8e47b78bba1fe1b31faee8f12aafd95385f6d6a8b108b03e92f5d743bb29f" +checksum = "b13816663794beb15c8a4721c15252eb21f3b3233525684f60c7888837a98ff4" dependencies = [ "miden-air", "miden-core", - "thiserror 2.0.17", + "thiserror", "tracing", "winter-verifier", ] @@ -2704,7 +2700,7 @@ checksum = "9d4cfab04baffdda3fb9eafa5f873604059b89a1699aa95e4f1057397a69f0b5" dependencies = [ "miden-formatting", "smallvec", - "thiserror 2.0.17", + "thiserror", ] [[package]] @@ -2734,7 +2730,7 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2861,7 +2857,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3027,7 +3023,7 @@ dependencies = [ "regex", "regex-syntax", "structmeta", - "syn", + "syn 2.0.111", ] [[package]] @@ -3103,7 +3099,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3248,7 +3244,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.111", ] [[package]] @@ -3276,13 +3272,14 @@ dependencies = [ ] [[package]] -name = "prost" -version = "0.13.5" +name = "proptest-derive" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" dependencies = [ - "bytes", - "prost-derive 0.13.5", + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] @@ -3292,7 +3289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" dependencies = [ "bytes", - "prost-derive 0.14.1", + "prost-derive", ] [[package]] @@ -3308,28 +3305,15 @@ dependencies = [ "once_cell", "petgraph", "prettyplease", - "prost 0.14.1", - "prost-types 0.14.1", + "prost", + "prost-types", "pulldown-cmark", "pulldown-cmark-to-cmark", "regex", - "syn", + "syn 2.0.111", "tempfile", ] -[[package]] -name = "prost-derive" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "prost-derive" version = "0.14.1" @@ -3340,20 +3324,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", -] - -[[package]] -name = "prost-reflect" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5edd582b62f5cde844716e66d92565d7faf7ab1445c8cebce6e00fba83ddb2" -dependencies = [ - "logos 0.14.4", - "miette", - "once_cell", - "prost 0.13.5", - "prost-types 0.13.5", + "syn 2.0.111", ] [[package]] @@ -3362,19 +3333,10 @@ version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89455ef41ed200cafc47c76c552ee7792370ac420497e551f16123a9135f76e" dependencies = [ - "logos 0.15.1", + "logos", "miette", - "prost 0.14.1", - "prost-types 0.14.1", -] - -[[package]] -name = "prost-types" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" -dependencies = [ - "prost 0.13.5", + "prost", + "prost-types", ] [[package]] @@ -3383,22 +3345,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" dependencies = [ - "prost 0.14.1", -] - -[[package]] -name = "protox" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f352af331bf637b8ecc720f7c87bf903d2571fa2e14a66e9b2558846864b54a" -dependencies = [ - "bytes", - "miette", - "prost 0.13.5", - "prost-reflect 0.14.7", - "prost-types 0.13.5", - "protox-parse 0.7.0", - "thiserror 1.0.69", + "prost", ] [[package]] @@ -3409,23 +3356,11 @@ checksum = "8555716f64c546306ddf3383065dc40d4232609e79e0a4c50e94e87d54f30fb4" dependencies = [ "bytes", "miette", - "prost 0.14.1", - "prost-reflect 0.16.3", - "prost-types 0.14.1", - "protox-parse 0.9.0", - "thiserror 2.0.17", -] - -[[package]] -name = "protox-parse" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3a462d115462c080ae000c29a47f0b3985737e5d3a995fcdbcaa5c782068dde" -dependencies = [ - "logos 0.14.4", - "miette", - "prost-types 0.13.5", - "thiserror 1.0.69", + "prost", + "prost-reflect", + "prost-types", + "protox-parse", + "thiserror", ] [[package]] @@ -3434,10 +3369,10 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "072eee358134396a4643dff81cfff1c255c9fbd3fb296be14bdb6a26f9156366" dependencies = [ - "logos 0.15.1", + "logos", "miette", - "prost-types 0.14.1", - "thiserror 2.0.17", + "prost-types", + "thiserror", ] [[package]] @@ -3586,7 +3521,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3956,7 +3891,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3991,7 +3926,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4043,7 +3978,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4207,7 +4142,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn", + "syn 2.0.111", ] [[package]] @@ -4218,7 +4153,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4239,7 +4174,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4269,6 +4204,17 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.111" @@ -4294,7 +4240,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4375,7 +4321,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.17", + "thiserror", "tokio", "tokio-stream", "tokio-util", @@ -4403,33 +4349,13 @@ dependencies = [ "unicode-width 0.2.2", ] -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - [[package]] name = "thiserror" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.17", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] @@ -4440,7 +4366,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4526,7 +4452,7 @@ checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4553,7 +4479,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4711,7 +4637,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4720,7 +4646,7 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a82868bf299e0a1d2e8dce0dc33a46c02d6f045b2c1f1d6cc8dc3d0bf1812ef" dependencies = [ - "prost 0.14.1", + "prost", "tokio", "tokio-stream", "tonic", @@ -4734,7 +4660,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" dependencies = [ "bytes", - "prost 0.14.1", + "prost", "tonic", ] @@ -4747,9 +4673,9 @@ dependencies = [ "prettyplease", "proc-macro2", "prost-build", - "prost-types 0.14.1", + "prost-types", "quote", - "syn", + "syn 2.0.111", "tempfile", "tonic-build", ] @@ -4770,7 +4696,7 @@ dependencies = [ "httparse", "js-sys", "pin-project", - "thiserror 2.0.17", + "thiserror", "tonic", "tower-service", "wasm-bindgen", @@ -4846,7 +4772,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -5193,7 +5119,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.111", "wasm-bindgen-shared", ] @@ -5357,7 +5283,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -5368,7 +5294,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -5730,7 +5656,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d31a19dae58475d019850e25b0170e94b16d382fbf6afee9c0e80fdc935e73e" dependencies = [ "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -5854,7 +5780,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", "synstructure", ] @@ -5875,7 +5801,7 @@ checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -5895,7 +5821,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", "synstructure", ] @@ -5916,7 +5842,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -5949,5 +5875,5 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] diff --git a/Cargo.toml b/Cargo.toml index 09d310b..a5e0c91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,8 @@ anyhow = "1" bon = { default-features = false, version = "3" } chrono = { default-features = false, version = "0.4" } dissolve-derive = "0.1.4" -miden-client = { default-features = false, version = "0.12" } -miden-client-sqlite-store = "0.12" +miden-client = { default-features = false, version = "0.13" } +miden-client-sqlite-store = "0.13" rand = "0.9" serde = { default-features = false, version = "1" } serde_with = { default-features = false, version = "3" } diff --git a/PSM.Migration.md b/PSM.Migration.md new file mode 100644 index 0000000..004f7f7 --- /dev/null +++ b/PSM.Migration.md @@ -0,0 +1,401 @@ +# Migration Plan: Coordinator API to PSM (MultisigClient SDK) + +## Context + +The MultiSig frontend currently uses a coordinator REST server (`/api/v1/...` endpoints) for all account and transaction operations. We're migrating to use the `@openzeppelin/miden-multisig-client` SDK directly, which talks to a PSM (Private State Manager) endpoint. This eliminates the coordinator server dependency and aligns with the architecture in `private-state-manager/examples/web/`. + +**Key changes:** +- Replace coordinator REST API calls with MultisigClient SDK calls +- Switch from `@demox-labs/miden-sdk` v0.12 to `@miden-sdk/miden-sdk` v0.13 +- Switch network from testnet to devnet +- Auto-generate local signer keys (Falcon + ECDSA) on app load +- Add Para wallet support alongside existing Miden Wallet adapter +- Replace manual secret key ConnectWalletModal with wallet source selector +- Add PSM endpoint selector/editor (ported from PSM app Header) + +--- + +## Step 1: Update Dependencies + +**File:** `bin/coordinator-frontend/package.json` + +Remove: +- `@demox-labs/miden-sdk` (v0.12.3) + +Add: +- `@miden-sdk/miden-sdk` (^0.13.0) +- `@openzeppelin/miden-multisig-client` (^0.13.0) +- `@openzeppelin/psm-client` (^0.13.0) +- `@miden-sdk/miden-para` (^0.13.0) - Para wallet Miden integration +- `@miden-sdk/use-miden-para-react` (^0.13.0) - Para React hook +- `@getpara/react-sdk-lite` (^2.2.0) - Para modal + provider +- `@tanstack/react-query` (^5.0.0) - required by Para SDK +- `sonner` (for toast notifications) + +Keep: +- `@demox-labs/miden-wallet-adapter*` packages (still used for Miden Wallet integration) +- All existing UI dependencies (framer-motion, redux, tailwind, etc.) + +> **Note:** If `@openzeppelin/miden-multisig-client` or `@openzeppelin/psm-client` aren't on npm yet, we'll install from the local `private-state-manager` monorepo via `file:` references. We do NOT modify private-state-manager itself. + +--- + +## Step 2: Port Library Functions (from PSM app) + +### Create `src/lib/initClient.ts` +Port from `private-state-manager/examples/web/src/lib/initClient.ts`: +- `clearMidenDatabase(dbName)` - deletes IndexedDB for fresh start +- `createWebClient(rpcUrl)` - creates WebClient from `@miden-sdk/miden-sdk`, syncs state +- `initializeSigner()` - generates Falcon + ECDSA key pairs, returns `SignerInfo` + +### Create `src/lib/multisigApi.ts` +Port from `private-state-manager/examples/web/src/lib/multisigApi.ts`: +- `createSigner(signerInfo, scheme, external?)` - creates FalconSigner/EcdsaSigner/ParaSigner/MidenWalletSigner +- `initMultisigClient(webClient, psmEndpoint, scheme?)` - initializes MultisigClient +- `createMultisigAccount(client, commitment, otherCommitments, threshold, ...)` - creates account +- `loadMultisigAccount(client, accountId, signer, psmPublicKey?)` - loads existing account + +### Create `src/lib/helpers.ts` +Port from `private-state-manager/examples/web/src/lib/helpers.ts`: +- `normalizeCommitment(hex)` - validates and normalizes hex commitment strings +- `truncateHex(hex)` - truncates hex for display +- `copyToClipboard(text)` - clipboard helper + +### Create `src/lib/errors.ts` +Port from `private-state-manager/examples/web/src/lib/errors.ts`: +- `formatError(err, prefix?)` - standardized error formatting +- `classifyWalletError(err)` - classifies wallet-specific errors + +### Create `src/lib/procedures.ts` +Port from `private-state-manager/examples/web/src/lib/procedures.ts`: +- `USER_PROCEDURES` - procedure info array +- `getProposalProcedure(proposalType)` - maps proposal type to procedure +- `getEffectiveThreshold(proposalType, defaultThreshold, procedureThresholds)` - gets effective threshold + +--- + +## Step 3: Port Wallet Hooks (from PSM app) + +### Create `src/hooks/useParaSession.ts` +Port from `private-state-manager/examples/web/src/hooks/useParaSession.ts`: +- Uses `useParaMiden` from `@miden-sdk/use-miden-para-react` +- Derives ECDSA commitment from Para wallet's public key +- Returns `{ session: ExternalWalletState, paraClient, getWalletId }` + +### Create `src/hooks/useMidenWallet.ts` +Port from `private-state-manager/examples/web/src/hooks/useMidenWallet.ts`: +- Wraps `MidenWalletAdapter` from `@demox-labs/miden-wallet-adapter-miden` +- Derives commitment from wallet's public key via `PublicKeyFormat.parse()` +- Returns `{ session, connect, disconnect, signBytes, connectError }` + +### Create `src/wallets/types.ts` +Port from `private-state-manager/examples/web/src/wallets/types.ts`: +- `WalletSource = 'local' | 'para' | 'miden-wallet'` +- `ExternalWalletState { source, connected, publicKey, commitment, scheme }` + +--- + +## Step 4: Create Configuration + +### Create `src/config/psm.ts` +```ts +export const PSM_ENDPOINT = process.env.NEXT_PUBLIC_PSM_ENDPOINT || 'https://psm-stg.openzeppelin.com'; +export const MIDEN_RPC_URL = process.env.NEXT_PUBLIC_MIDEN_RPC_URL || 'https://rpc.devnet.miden.io'; +export const MIDEN_DB_NAME = 'MidenClientDB'; +export const PARA_API_KEY = process.env.NEXT_PUBLIC_PARA_API_KEY || ''; +export const PARA_ENVIRONMENT = (process.env.NEXT_PUBLIC_PARA_ENVIRONMENT || 'development') as 'development' | 'production'; +``` + +### Delete `src/config/api.ts` (coordinator API config no longer needed) + +--- + +## Step 5: Create MultisigContext (replaces MidenClientContext) + +### Create `src/contexts/MultisigContext.tsx` + +Central context modeled after `App.tsx` in the PSM app. Manages all PSM/SDK state and operations. + +**State it provides:** +- `webClient: WebClient | null` - Miden blockchain client +- `multisigClient: MultisigClient | null` - PSM multisig client +- `signer: SignerInfo | null` - local signer key pairs (falcon + ecdsa commitments) +- `multisig: Multisig | null` - loaded/created multisig account instance +- `psmStatus: 'connecting' | 'connected' | 'error'` +- `psmUrl: string` - current PSM endpoint URL (editable) +- `psmCommitment: string` - PSM commitment for account creation +- `psmPublicKey: string | undefined` - PSM ECDSA public key +- `proposals: TransactionProposal[]` - synced proposals +- `consumableNotes: array` - available notes +- `detectedConfig: DetectedMultisigConfig | null` - threshold, signerCommitments, vaultBalances +- `psmState: AccountState | null` - PSM account state +- `error: string | null` +- `walletSource: WalletSource` - 'local' | 'para' | 'miden-wallet' +- `activeCommitment: string | null` - commitment from active wallet source +- `activeScheme: SignatureScheme` - current signature scheme +- Loading flags: `creating`, `loadingAccount`, `syncingState`, `signingProposal`, `executingProposal`, `generatingSigner` + +**Operations it provides:** +- `handleCreate(otherCommitments, threshold, procedureThresholds?, scheme?)` - create account + register on PSM +- `handleLoad(accountId, scheme?)` - load account + syncAll +- `handleSync()` - sync state with PSM + chain +- `handleSignProposal(proposalId)` - sign a proposal +- `handleExecuteProposal(proposalId)` - execute a proposal +- `handleCreateSendProposal(recipientId, faucetId, amount)` - create send proposal +- `handleCreateConsumeNotesProposal(noteIds)` - create consume notes proposal +- `handleCreateAddSignerProposal(commitment, increaseThreshold)` - add signer proposal +- `handleCreateRemoveSignerProposal(signerToRemove, newThreshold?)` - remove signer proposal +- `handleCreateChangeThresholdProposal(newThreshold)` - change threshold proposal +- `handleCreateSwitchPsmProposal(newEndpoint, newPubkey)` - switch PSM proposal +- `handleDisconnect()` - clear account state +- `setWalletSource(source)` - switch between local/para/miden-wallet +- `connectToPsm(url)` - reconnect to different PSM endpoint +- `setPsmUrl(url)` - update PSM URL + +**Initialization (useEffect on mount):** +1. `clearMidenDatabase()` +2. `createWebClient(MIDEN_RPC_URL)` - connect to devnet +3. `connectToPsm(PSM_ENDPOINT, client)` - initialize MultisigClient +4. `initializeSigner()` - generate local Falcon + ECDSA key pairs + +**Wallet source management (from PSM App.tsx):** +- Auto-switch to Para when Para modal connects +- Auto-switch to Miden Wallet when it connects +- `activeCommitment` computed from current wallet source +- `activeScheme` computed from current wallet source +- `buildExternalParams()` builds the correct signer params for SDK calls + +--- + +## Step 6: Update Providers + +**File:** `src/components/Providers.tsx` + +Replace the entire provider tree to match PSM app's setup: +- Outermost: `QueryClientProvider` (for `@tanstack/react-query`, required by Para) +- `ParaProvider` with `paraClientConfig` (API key + environment) +- `Provider` (Redux) - keep for wallet form state only +- `MultisigProvider` (new, replaces MidenClientProvider + MidenSdkProvider) +- Remove: `MidenSdkProvider`, `WalletProvider`, `WalletModalProvider` (replaced by new hooks) + +The Miden Wallet adapter is now managed via `useMidenWallet` hook inside MultisigContext, not via a separate WalletProvider. + +--- + +## Step 7: Update Account Creation Flow + +**File:** `src/app/login/createNewAccount/page.tsx` + +- Step 2 (Add Signers): Keep address + public key fields. The "public key" field value is used as the signer commitment. Address is stored for display/reference. +- Step 4 (Create Account): Replace `createMultiSigWallet(formData)` with: + ```ts + const { handleCreate } = useMultisig(); + const commitments = formData.signerPublicKeys; // these are commitments + await handleCreate(commitments, parseInt(formData.signatureThreshold), undefined, activeScheme); + ``` +- After creation, store `multisig.accountId` via `setWalletId()` +- Remove import of `createMultiSigWallet` from `services/walletApi` + +--- + +## Step 8: Update Account Loading Flow + +**File:** `src/app/login/loadExistingAccount/page.tsx` + +- Replace `Address.fromBech32()` + address conversion logic with: + ```ts + const { handleLoad } = useMultisig(); + await handleLoad(accountId, activeScheme); + ``` +- Input field changes from "Account Address" (bech32) to "Account ID" (hex, 0x prefix optional) +- Remove import of `Address`, `NetworkId` from old SDK + +--- + +## Step 9: Update Dashboard - Home Page + +**File:** `src/app/dashboard/home/page.tsx` + +Replace coordinator API thunks with MultisigContext: +- Replace `useWalletData()` hook with `useMultisig()` context +- `detectedConfig.signerCommitments` -> approvers/signers list +- `detectedConfig.threshold` -> signature threshold +- `detectedConfig.vaultBalances` -> asset balances +- `proposals` filtered by status -> pending/confirmed transactions +- Derive stats from proposals array instead of `fetchTransactionStatsThunk` + +--- + +## Step 10: Update Dashboard - Assets, Transactions, Settings Pages + +**File:** `src/app/dashboard/assets/page.tsx` +- Use `detectedConfig.vaultBalances` from MultisigContext + +**File:** `src/app/dashboard/transactions/page.tsx` +- Use `proposals` from MultisigContext + +**File:** `src/app/dashboard/settings/page.tsx` and sub-components: +- `Signers.tsx` - use `detectedConfig.signerCommitments` (displayed as hex) +- `Security.tsx` - replace `WalletInfo` / `useWallet()` with MultisigContext wallet state +- `General.tsx` - use MultisigContext data + +--- + +## Step 11: Update Dashboard Components + +**File:** `src/app/dashboard/components/PendingActions.tsx` +- Replace `useWallet()` from `@demox-labs/miden-wallet-adapter` with MultisigContext +- Use `proposals` from context, `handleSignProposal`, `handleExecuteProposal` + +**File:** `src/app/dashboard/components/RecentTransactions.tsx` +- Use `proposals` from MultisigContext (filter executed/recent) + +**File:** `src/app/dashboard/components/Taskbar.tsx` +- Replace `ConnectWalletModal` (secret key input) with wallet source indicator +- Show local commitment (copyable), wallet source selector, Para/Miden Wallet connect buttons +- Add PSM endpoint selector/editor (from PSM Header.tsx) with status badge +- Use `handleSync` from MultisigContext for refresh +- Replace `useWalletData()` with `useMultisig()` context + +--- + +## Step 12: Update Transaction Interactions + +**File:** `src/interactions/InitiateFundTransfer.tsx` +- Replace `proposeTransactionWithTxBzThunk` with `handleCreateSendProposal(recipientId, faucetId, amount)` + +**File:** `src/interactions/ApproveFundTransfer.tsx` +- Replace signature API with `handleSignProposal(proposalId)` + +**File:** `src/interactions/signTransaction.tsx` +- Replace signature/execution logic with `handleSignProposal` and `handleExecuteProposal` + +--- + +## Step 13: Update Types + +**File:** `src/types/` directory + +Add new types (ported from PSM app): +- `SignerKeyInfo { commitment: string; secretKey: AuthSecretKey }` +- `SignerInfo { falcon: SignerKeyInfo; ecdsa: SignerKeyInfo; activeScheme: SignatureScheme }` +- `WalletSource = 'local' | 'para' | 'miden-wallet'` +- `ExternalWalletState { source, connected, publicKey, commitment, scheme }` +- Re-export `TransactionProposal`, `DetectedMultisigConfig`, `AccountState`, `SignatureScheme` from `@openzeppelin/miden-multisig-client` + +Update existing type files to accommodate proposals (instead of coordinator transactions). + +--- + +## Step 14: Clean Up - Remove Coordinator API Layer + +### Delete files: +- `src/services/walletApi.ts` - coordinator wallet API +- `src/services/transactionApi.ts` - coordinator transaction API +- `src/services/signatureApi.ts` - coordinator signature API +- `src/config/api.ts` - coordinator URL config +- `src/contexts/MidenClientContext.tsx` - replaced by MultisigContext +- `src/hooks/useMidenSdk.tsx` - replaced by MultisigContext +- `lib/miden-client.ts` - replaced by src/lib/initClient.ts +- `src/components/ConnectWalletModal.tsx` - replaced by wallet source in Taskbar +- `src/hooks/useWalletData.ts` - replaced by MultisigContext + +### Simplify Redux: +- `src/store/slices/walletSlice.ts` - keep form data for wizard only; remove approver/walletData state and API thunks +- `src/store/slices/transactionSlice.ts` - delete (replaced by context proposals) +- `src/store/slices/signatureSlice.ts` - delete (replaced by context) +- `src/store/slices/walletStatsSlice.ts` - delete (stats derived from context) +- `src/store/index.ts` - update to only include walletSlice + +--- + +## Step 15: Update WASM & Next.js Config + +**File:** `next.config.mjs` +- Update WASM copy paths for `@miden-sdk/miden-sdk` v0.13 (may use different WASM file) +- Keep `asyncWebAssembly` experiment and CORS headers +- Add webpack resolve alias for `@miden-sdk/miden-sdk` if needed (similar to PSM's vite config) +- Stub Para optional modules (`@getpara/evm-wallet-connectors`, etc.) via webpack plugin + +**File:** `public/miden_client_web.wasm` +- Replace/update from the new SDK's distribution + +--- + +## Step 16: Update Middleware & Auth + +**File:** `src/middleware.ts` +- Keep same cookie-based auth pattern +- Wallet ID is now a hex account ID instead of bech32 address + +**File:** `src/hooks/useAuth.ts` +- No structural changes; stores/retrieves `currentWalletId` +- Value format changes from bech32 to hex account ID + +--- + +## Files Summary + +### New files (12): +- `src/lib/initClient.ts` +- `src/lib/multisigApi.ts` +- `src/lib/helpers.ts` +- `src/lib/errors.ts` +- `src/lib/procedures.ts` +- `src/config/psm.ts` +- `src/contexts/MultisigContext.tsx` +- `src/hooks/useParaSession.ts` +- `src/hooks/useMidenWallet.ts` (new version, replaces old wallet adapter wrapper) +- `src/wallets/types.ts` +- `src/wallets/index.ts` + +### Modified files (~17): +- `package.json` +- `next.config.mjs` +- `src/components/Providers.tsx` +- `src/app/login/createNewAccount/page.tsx` +- `src/app/login/loadExistingAccount/page.tsx` +- `src/app/dashboard/home/page.tsx` +- `src/app/dashboard/assets/page.tsx` +- `src/app/dashboard/transactions/page.tsx` +- `src/app/dashboard/settings/page.tsx` +- `src/app/dashboard/settings/components/Signers.tsx` +- `src/app/dashboard/settings/components/Security.tsx` +- `src/app/dashboard/settings/components/General.tsx` +- `src/app/dashboard/components/PendingActions.tsx` +- `src/app/dashboard/components/RecentTransactions.tsx` +- `src/app/dashboard/components/Taskbar.tsx` +- `src/interactions/InitiateFundTransfer.tsx` +- `src/interactions/ApproveFundTransfer.tsx` +- `src/interactions/signTransaction.tsx` +- `src/store/index.ts` +- `src/store/slices/walletSlice.ts` +- `src/types/` files + +### Deleted files (9): +- `src/services/walletApi.ts` +- `src/services/transactionApi.ts` +- `src/services/signatureApi.ts` +- `src/config/api.ts` +- `src/contexts/MidenClientContext.tsx` +- `src/hooks/useMidenSdk.tsx` +- `src/hooks/useWalletData.ts` +- `src/components/ConnectWalletModal.tsx` +- `lib/miden-client.ts` + +--- + +## Verification + +1. **Install & build:** `npm install && npm run build` completes without errors +2. **Dev server:** `npm run dev` starts at localhost:3000 +3. **Login page:** Shows create/load options, console shows PSM connected + local keys generated +4. **Create flow:** Enter account name, threshold, signer commitments, create calls PSM SDK +5. **Load flow:** Enter hex account ID, loads from PSM, syncs proposals/state +6. **Dashboard:** Shows vault balances, proposals, signer commitments from `detectedConfig` +7. **Wallet sources:** Can switch between local/Para/Miden Wallet; commitment updates accordingly +8. **Para wallet:** Para modal opens, connects, derives ECDSA commitment, can sign proposals +9. **Miden Wallet:** Browser extension connects, derives commitment, can sign proposals +10. **Transactions:** Can create send proposals, sign with any wallet source, execute proposals +11. **No coordinator dependency:** App functions without the coordinator server running +12. **PSM selector:** Can view/edit PSM endpoint URL and reconnect from the Taskbar diff --git a/bin/coordinator-frontend/lib/miden-client.ts b/bin/coordinator-frontend/lib/miden-client.ts deleted file mode 100644 index 2a78960..0000000 --- a/bin/coordinator-frontend/lib/miden-client.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { - WebClient, - ConsumableNoteRecord, - Account -} from "@demox-labs/miden-sdk"; - -export class MidenWebClientHandle { - private webClient: WebClient | null = null; - private account: Account | null = null; - - constructor() { - this.webClient = null; - this.account = null; - } - - /** - * Initialize the Miden Web Client - */ - async initialize(): Promise { - try { - console.log("Initializing Miden Web Client..."); - const nodeEndpoint = process.env.NEXT_PUBLIC_MIDEN_NODE_ENDPOINT || "https://rpc.testnet.miden.io:443"; - console.log(`Using Miden node endpoint: ${nodeEndpoint}`); - - try { - this.webClient = await WebClient.createClient(nodeEndpoint); - } catch (error: any) { - console.error("Error creating Miden client:", error); - - // Check for specific IndexedDB upgrade error or generic WebStore initialization failure - // The SDK might wrap the underlying DB error into "Failed to initialize WebStore" - if (error?.message?.includes("UpgradeError") || - error?.message?.includes("Not yet support for changing primary key") || - error?.message?.includes("Failed to initialize WebStore") || - error?.toString().includes("Failed to initialize WebStore")) { - console.warn("Database schema mismatch or init failure detected. Attempting to clear database..."); - - const DB_NAME = "MidenClientDB"; - - try { - await new Promise((resolve, reject) => { - const req = indexedDB.deleteDatabase(DB_NAME); - - // Add a timeout to prevent hanging - const timeoutId = setTimeout(() => { - reject(new Error("Database deletion timed out. Please close other tabs and reload.")); - }, 5000); - - req.onsuccess = () => { - clearTimeout(timeoutId); - console.log(`Successfully deleted database: ${DB_NAME}`); - resolve(); - }; - - req.onerror = () => { - clearTimeout(timeoutId); - console.error(`Failed to delete database: ${DB_NAME}`); - reject(req.error); - }; - - req.onblocked = () => { - console.warn(`Database deletion blocked: ${DB_NAME}. Please close other tabs using this app.`); - // We don't reject immediately on blocked, as it might unblock if user closes tabs, - // but the timeout will catch it if it takes too long. - }; - }); - - // Retry initialization - console.log("Retrying initialization after database cleanup..."); - this.webClient = await WebClient.createClient(nodeEndpoint); - } catch (cleanupError) { - console.error("Failed to recover from database error:", cleanupError); - throw cleanupError; // Re-throw to be caught by outer catch - } - } else { - throw error; - } - } - - console.log("Miden Web Client initialized successfully!"); - - return true; - } catch (error) { - console.error("Failed to initialize Miden Web Client:", error); - return false; - } - } - - /** - Sync state with the Miden chain - */ - async syncState(): Promise { - try { - console.log("\nSyncing state with Miden chain..."); - await this.webClient!.syncState(); - console.log("State synced successfully!"); - return true; - } catch (error) { - console.error("Failed to sync state:", error); - return false; - } - } - - /** - * Get consumable notes for the account - */ - async getConsumableNotes(): Promise { - try { - console.log("\nFetching consumable notes..."); - const consumableNotes = await this.webClient!.getConsumableNotes(this.account?.id()); - console.log(consumableNotes); - console.log(`Found ${consumableNotes.length} consumable notes`); - return consumableNotes; - } catch (error) { - console.error("Failed to get consumable notes:", error); - return []; - } - } - - // Getters for React components - getAccount() { - return this.account; - } - - getWebClient() { - return this.webClient; - } - - isInitialized(): boolean { - return this.webClient !== null; - } - - hasAccount(): boolean { - return this.account !== null; - } -} \ No newline at end of file diff --git a/bin/coordinator-frontend/next.config.mjs b/bin/coordinator-frontend/next.config.mjs index 540813b..fb778ac 100644 --- a/bin/coordinator-frontend/next.config.mjs +++ b/bin/coordinator-frontend/next.config.mjs @@ -1,28 +1,28 @@ import CopyWebpackPlugin from 'copy-webpack-plugin'; import path from 'path'; -import { fileURLToPath } from 'url'; +import { fileURLToPath, pathToFileURL } from 'url'; +import { createRequire } from 'module'; +import webpack from 'webpack'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); +const require = createRequire(import.meta.url); /** @type {import('next').NextConfig} */ const nextConfig = { output: 'standalone', - // Disable static optimization completely to avoid WASM loading issues - experimental: { - // This is not experimental anymore in Next.js 15, but keep for compatibility + env: { + // Expose VITE_PARA_API_KEY as NEXT_PUBLIC_PARA_API_KEY for browser access + NEXT_PUBLIC_PARA_API_KEY: process.env.VITE_PARA_API_KEY || process.env.NEXT_PUBLIC_PARA_API_KEY || '', }, - // Generate all pages dynamically (no static generation) - // This prevents WASM loading during build + experimental: {}, generateBuildId: async () => { return 'build-id' }, eslint: { - // Don't fail the build on ESLint errors during production builds ignoreDuringBuilds: true, }, typescript: { - // Don't fail the build on TypeScript errors during production builds ignoreBuildErrors: true, }, webpack: (config, { isServer }) => { @@ -44,6 +44,28 @@ const nextConfig = { path: false, } + // Stub out optional Para wallet connector modules that aren't used + // These are optionally required by @getpara/react-sdk-lite + const emptyStub = path.join(__dirname, 'src/stubs/empty.js'); + config.plugins.push( + new webpack.NormalModuleReplacementPlugin( + /^@getpara\/evm-wallet-connectors$/, + emptyStub + ), + new webpack.NormalModuleReplacementPlugin( + /^@getpara\/solana-wallet-connectors$/, + emptyStub + ), + new webpack.NormalModuleReplacementPlugin( + /^@getpara\/cosmos-wallet-connectors$/, + emptyStub + ), + new webpack.NormalModuleReplacementPlugin( + /^@farcaster\/miniapp-sdk$/, + emptyStub + ), + ) + // Copy WASM file to multiple locations for better accessibility config.plugins.push( new CopyWebpackPlugin({ @@ -51,14 +73,17 @@ const nextConfig = { { from: path.join(__dirname, 'public/miden_client_web.wasm'), to: path.join(__dirname, '.next/static/wasm/miden_client_web.wasm'), + noErrorOnMissing: true, }, { from: path.join(__dirname, 'public/miden_client_web.wasm'), to: path.join(__dirname, '.next/static/media/miden_client_web.wasm'), + noErrorOnMissing: true, }, { from: path.join(__dirname, 'public/miden_client_web.wasm'), to: path.join(__dirname, 'public/static/wasm/miden_client_web.wasm'), + noErrorOnMissing: true, }, ], }) @@ -121,4 +146,4 @@ const nextConfig = { }, } -export default nextConfig \ No newline at end of file +export default nextConfig diff --git a/bin/coordinator-frontend/package-lock.json b/bin/coordinator-frontend/package-lock.json index 6f8881d..d757da1 100644 --- a/bin/coordinator-frontend/package-lock.json +++ b/bin/coordinator-frontend/package-lock.json @@ -8,16 +8,22 @@ "name": "miden_wallet", "version": "0.1.0", "dependencies": { - "@demox-labs/miden-sdk": "^0.12.3", - "@demox-labs/miden-wallet-adapter": "^0.10.0", "@demox-labs/miden-wallet-adapter-base": "^0.10.0", "@demox-labs/miden-wallet-adapter-miden": "^0.10.0", + "@getpara/react-sdk-lite": "^2.2.0", + "@miden-sdk/miden-para": "^0.13.0", + "@miden-sdk/miden-sdk": "^0.13.0", + "@miden-sdk/use-miden-para-react": "^0.13.0", + "@openzeppelin/miden-multisig-client": "^0.13.0", + "@openzeppelin/psm-client": "^0.13.0", "@reduxjs/toolkit": "^2.8.2", + "@tanstack/react-query": "^5.0.0", "framer-motion": "^12.23.12", "next": "15.4.5", "react": "^19.1.1", "react-dom": "^19.1.1", "react-redux": "^9.2.0", + "sonner": "^2.0.7", "tailwind-scrollbar": "^3.0.5" }, "devDependencies": { @@ -46,114 +52,202 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@demox-labs/miden-sdk": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/@demox-labs/miden-sdk/-/miden-sdk-0.12.3.tgz", - "integrity": "sha512-iHomtLaZrY1cb8FVevij9x2foXo33qxRnUNNszKDmdlvgEgt1XmZlZUP8+bb4OFPMJF5lRsJaLcfd5pODvF6JQ==", - "license": "MIT*", - "dependencies": { - "@rollup/plugin-typescript": "^12.3.0", - "dexie": "^4.0.1", - "glob": "^11.0.0" - } + "node_modules/@celo/base": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@celo/base/-/base-7.0.4.tgz", + "integrity": "sha512-LUWVdqchXVKlp9h4Kh190wKE6DDy+zAREfVbKBIH/AWvrGv3EWQSUSDj3C8vrZjJ5wiCGt/ws+5+39Iao/W02Q==", + "license": "Apache-2.0" }, - "node_modules/@demox-labs/miden-sdk/node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", - "license": "ISC", + "node_modules/@celo/utils": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@celo/utils/-/utils-8.0.3.tgz", + "integrity": "sha512-eHXSqRGWzXLGnfqq4eq37JAUnalqX5EIhlXyqSmxtIxc0Shkzlq7hrMpxI4diUS/T2zCcs9OQtr2ihjGxzqtqA==", + "license": "Apache-2.0", "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" + "@celo/base": "^7.0.3", + "@ethereumjs/rlp": "^5.0.2", + "@ethereumjs/util": "8.0.5", + "@noble/ciphers": "1.1.3", + "@noble/curves": "1.3.0", + "@noble/hashes": "1.3.3", + "@types/bn.js": "^5.1.0", + "@types/node": "^18.7.16", + "bignumber.js": "^9.0.0", + "fp-ts": "2.16.9", + "io-ts": "2.0.1", + "web3-eth-abi": "1.10.4", + "web3-utils": "1.10.4" + } + }, + "node_modules/@celo/utils/node_modules/@ethereumjs/util": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.0.5.tgz", + "integrity": "sha512-259rXKK3b3D8HRVdRmlOEi6QFvwxdt304hhrEAmpZhsj7ufXEOTIc9JRZPMnXatKjECokdLNBcDOFBeBSzAIaw==", + "license": "MPL-2.0", + "dependencies": { + "@chainsafe/ssz": "0.9.4", + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^1.1.2" }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@celo/utils/node_modules/@ethereumjs/util/node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "license": "MPL-2.0", "bin": { - "glob": "dist/esm/bin.mjs" + "rlp": "bin/rlp" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=14" } }, - "node_modules/@demox-labs/miden-sdk/node_modules/jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, + "node_modules/@celo/utils/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "license": "MIT", "engines": { - "node": "20 || >=22" + "node": ">= 16" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@demox-labs/miden-sdk/node_modules/lru-cache": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.0.tgz", - "integrity": "sha512-1oZqnd6TxE/NZyRZ21u+10DKV5sMqT0rJFlPbytSH/0UaXLvE/EurTrd2nDnHif6E1l8N9mothtWs/Um3RijjA==", - "license": "ISC", + "node_modules/@celo/utils/node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", "dependencies": { - "esbuild": "^0.25.9" - }, - "engines": { - "node": "20 || >=22" + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" } }, - "node_modules/@demox-labs/miden-sdk/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "license": "ISC", + "node_modules/@celo/utils/node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@celo/utils/node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" } }, - "node_modules/@demox-labs/miden-sdk/node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "license": "BlueOak-1.0.0", + "node_modules/@celo/utils/node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@celo/utils/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "license": "MIT", "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "undici-types": "~5.26.4" } }, - "node_modules/@demox-labs/miden-wallet-adapter": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@demox-labs/miden-wallet-adapter/-/miden-wallet-adapter-0.10.0.tgz", - "integrity": "sha512-pCyV1vnhwNebBf8MEI6Tz6pbnMe5Kt9I/XiwOskA200o1wK1Sp7z8b3MdLnjyXNe7CXn3gJJFdBqzbU96vF1Lg==", + "node_modules/@celo/utils/node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", "license": "MIT", "dependencies": { - "@demox-labs/miden-wallet-adapter-base": "^0.10.0", - "@demox-labs/miden-wallet-adapter-miden": "^0.10.0", - "@demox-labs/miden-wallet-adapter-react": "^0.10.0", - "@demox-labs/miden-wallet-adapter-reactui": "^0.10.0" - }, - "peerDependencies": { - "@types/react": "^19.0.0", - "@types/react-dom": "^19.0.0", - "react": "^19.1.1", - "react-dom": "^19.1.1" + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/@celo/utils/node_modules/ethereum-cryptography/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@celo/utils/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/@chainsafe/as-sha256": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz", + "integrity": "sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg==", + "license": "Apache-2.0" + }, + "node_modules/@chainsafe/persistent-merkle-tree": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz", + "integrity": "sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ==", + "license": "Apache-2.0", + "dependencies": { + "@chainsafe/as-sha256": "^0.3.1" + } + }, + "node_modules/@chainsafe/ssz": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.9.4.tgz", + "integrity": "sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ==", + "license": "Apache-2.0", + "dependencies": { + "@chainsafe/as-sha256": "^0.3.1", + "@chainsafe/persistent-merkle-tree": "^0.4.2", + "case": "^1.6.3" + } + }, + "node_modules/@cosmjs/encoding": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.32.4.tgz", + "integrity": "sha512-tjvaEy6ZGxJchiizzTn7HVRiyTg1i4CObRRaTRPknm5EalE13SV+TCHq38gIDfyUeden4fCuaBVEdBR5+ti7Hw==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "bech32": "^1.1.4", + "readonly-date": "^1.0.0" } }, "node_modules/@demox-labs/miden-wallet-adapter-base": { @@ -196,34 +290,6 @@ "node": "^18 || >=20" } }, - "node_modules/@demox-labs/miden-wallet-adapter-react": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@demox-labs/miden-wallet-adapter-react/-/miden-wallet-adapter-react-0.10.0.tgz", - "integrity": "sha512-498XMpvY5QyEDjYbS6CUjzgAuD7BZFL1LxYIWskIglK/TOEGWZEOINT+bsXgan4Bgd+ybfKcJNYY0aAmyPtc2A==", - "license": "MIT", - "dependencies": { - "@demox-labs/miden-wallet-adapter-base": "^0.10.0" - }, - "peerDependencies": { - "react": "^19.1.1" - } - }, - "node_modules/@demox-labs/miden-wallet-adapter-reactui": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@demox-labs/miden-wallet-adapter-reactui/-/miden-wallet-adapter-reactui-0.10.0.tgz", - "integrity": "sha512-SdHq7ic6S4GXFcuIg8mtSSS/iFQeLOeBqJYlXh3LrHMGfKVQll9nRhGT1ZvJlXfQdC3/aX3fCNDqsgNzfosTjg==", - "license": "MIT", - "dependencies": { - "@demox-labs/miden-wallet-adapter-base": "^0.10.0", - "@demox-labs/miden-wallet-adapter-react": "^0.10.0" - }, - "peerDependencies": { - "@types/react": "^19.0.0", - "@types/react-dom": "^19.0.0", - "react": "^19.1.1", - "react-dom": "^19.1.1" - } - }, "node_modules/@emnapi/core": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", @@ -257,561 +323,851 @@ "tslib": "^2.4.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", - "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", - "cpu": [ - "ppc64" - ], + "node_modules/@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@emotion/memoize": "^0.9.0" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", - "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", - "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", - "cpu": [ - "arm64" - ], + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", - "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", - "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">=18" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", - "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", - "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", - "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", - "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", - "cpu": [ - "arm" - ], + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", - "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/js": { + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", - "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", - "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@eslint/plugin-kit": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", - "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@ethereumjs/rlp": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz", + "integrity": "sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==", + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp.cjs" + }, "engines": { "node": ">=18" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", - "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@ethereumjs/util": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz", + "integrity": "sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==", + "license": "MPL-2.0", + "dependencies": { + "@ethereumjs/rlp": "^5.0.2", + "ethereum-cryptography": "^2.2.1" + }, "engines": { "node": ">=18" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", - "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", - "cpu": [ - "riscv64" + "node_modules/@ethersproject/abi": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.8.0.tgz", + "integrity": "sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@ethersproject/address": "^5.8.0", + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/constants": "^5.8.0", + "@ethersproject/hash": "^5.8.0", + "@ethersproject/keccak256": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/strings": "^5.8.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", - "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", - "cpu": [ - "s390x" + "node_modules/@ethersproject/abstract-provider": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz", + "integrity": "sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/networks": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/transactions": "^5.8.0", + "@ethersproject/web": "^5.8.0" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", - "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", - "cpu": [ - "x64" + "node_modules/@ethersproject/abstract-signer": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz", + "integrity": "sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@ethersproject/abstract-provider": "^5.8.0", + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", - "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", - "cpu": [ - "arm64" + "node_modules/@ethersproject/address": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.8.0.tgz", + "integrity": "sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/keccak256": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/rlp": "^5.8.0" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", - "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", - "cpu": [ - "x64" + "node_modules/@ethersproject/base64": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.8.0.tgz", + "integrity": "sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@ethersproject/bytes": "^5.8.0" } }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", - "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", - "cpu": [ - "arm64" + "node_modules/@ethersproject/bignumber": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.8.0.tgz", + "integrity": "sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "bn.js": "^5.2.1" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", - "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", - "cpu": [ - "x64" + "node_modules/@ethersproject/bytes": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz", + "integrity": "sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@ethersproject/logger": "^5.8.0" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", - "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", - "cpu": [ - "arm64" + "node_modules/@ethersproject/constants": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.8.0.tgz", + "integrity": "sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@ethersproject/bignumber": "^5.8.0" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", - "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", - "cpu": [ - "x64" + "node_modules/@ethersproject/hash": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.8.0.tgz", + "integrity": "sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@ethersproject/abstract-signer": "^5.8.0", + "@ethersproject/address": "^5.8.0", + "@ethersproject/base64": "^5.8.0", + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/keccak256": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/strings": "^5.8.0" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", - "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", - "cpu": [ - "arm64" + "node_modules/@ethersproject/keccak256": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.8.0.tgz", + "integrity": "sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "js-sha3": "0.8.0" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", - "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", - "cpu": [ - "ia32" + "node_modules/@ethersproject/logger": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz", + "integrity": "sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT" + }, + "node_modules/@ethersproject/networks": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.8.0.tgz", + "integrity": "sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], "license": "MIT", - "optional": true, - "os": [ - "win32" + "dependencies": { + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz", + "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], - "engines": { - "node": ">=18" + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.8.0" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", - "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", - "cpu": [ - "x64" + "node_modules/@ethersproject/rlp": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.8.0.tgz", + "integrity": "sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], "license": "MIT", - "optional": true, - "os": [ - "win32" + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz", + "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } ], - "engines": { - "node": ">=18" + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "bn.js": "^5.2.1", + "elliptic": "6.6.1", + "hash.js": "1.1.7" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", - "dev": true, + "node_modules/@ethersproject/strings": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.8.0.tgz", + "integrity": "sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/constants": "^5.8.0", + "@ethersproject/logger": "^5.8.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node_modules/@ethersproject/transactions": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.8.0.tgz", + "integrity": "sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/address": "^5.8.0", + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/constants": "^5.8.0", + "@ethersproject/keccak256": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/rlp": "^5.8.0", + "@ethersproject/signing-key": "^5.8.0" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, + "node_modules/@ethersproject/web": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.8.0.tgz", + "integrity": "sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "dependencies": { + "@ethersproject/base64": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "@ethersproject/strings": "^5.8.0" } }, - "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@getpara/core-components": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@getpara/core-components/-/core-components-2.9.0.tgz", + "integrity": "sha512-t60RCFcncqRtAwLy12XEeKEvMGtG2svCEXo0aPIXsmyl2dWd7lHU303w6APRH9vsx5QKjPnppVX14tM4Hs1+Lg==", "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" + "@stencil/core": "^4.7.0", + "color-blend": "^4.0.0", + "color2k": "^2.0.3", + "gsap": "^3.12.5", + "inputmask": "5.0.9", + "qrcode-with-logos": "1.1.1" + } + }, + "node_modules/@getpara/core-sdk": { + "version": "2.0.0-alpha.73", + "resolved": "https://registry.npmjs.org/@getpara/core-sdk/-/core-sdk-2.0.0-alpha.73.tgz", + "integrity": "sha512-tCRMVyLXLb0z0YDWEgclnII8k/aSjpSCcS3mrcc5NdWEVtj9E8QYpdFeYItDOq9WWTbzhWjFHbvRVlLwIw1oPQ==", + "peer": true, + "dependencies": { + "@celo/utils": "^8.0.2", + "@cosmjs/encoding": "^0.32.4", + "@ethereumjs/util": "^9.1.0", + "@getpara/user-management-client": "2.0.0-alpha.73", + "@noble/hashes": "^1.5.0", + "base64url": "^3.0.1", + "libphonenumber-js": "^1.11.7", + "node-forge": "^1.3.1", + "uuid": "^11.1.0" + } + }, + "node_modules/@getpara/react-common": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@getpara/react-common/-/react-common-2.9.0.tgz", + "integrity": "sha512-/KVzAKi5WAAQ2V4o65TeBll+gjzNOEVmMV7KnpsFzBk1YRlgcRBmgdFBwM7EKVg6n0XMJtlpQ6jr6ZguCcs6tQ==", + "dependencies": { + "@getpara/react-components": "2.9.0", + "@getpara/web-sdk": "2.9.0", + "@moonpay/moonpay-react": "^1.10.6", + "@ramp-network/ramp-instant-sdk": "^4.0.5", + "libphonenumber-js": "^1.11.7", + "styled-components": "^6.1.8", + "ua-parser-js": "^2.0.2" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@getpara/react-common/node_modules/@getpara/core-sdk": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@getpara/core-sdk/-/core-sdk-2.9.0.tgz", + "integrity": "sha512-PMjIVs28cBxEph7mm4dkU2Fhcoeah+8DXxUW3C7iuTqiz04EwWMtq0IvQJGW7oCAPuJ7oRL4vP2rpBgUWIgg0w==", + "dependencies": { + "@celo/utils": "^8.0.2", + "@cosmjs/encoding": "^0.32.4", + "@ethereumjs/util": "^9.1.0", + "@getpara/user-management-client": "2.9.0", + "@noble/hashes": "^1.5.0", + "base64url": "^3.0.1", + "libphonenumber-js": "^1.11.7", + "node-forge": "^1.3.1", + "uuid": "^11.1.0" + } + }, + "node_modules/@getpara/react-common/node_modules/@getpara/shared": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@getpara/shared/-/shared-1.10.0.tgz", + "integrity": "sha512-xSBzCrQEbm+p32aoIjZcKL+TrfNRRYvuwGQrzvEMLB+vUxvkPMWz2n1uifRKzIVb/1MtIX4lDv5WiNe8xqFS9A==" + }, + "node_modules/@getpara/react-common/node_modules/@getpara/user-management-client": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@getpara/user-management-client/-/user-management-client-2.9.0.tgz", + "integrity": "sha512-/DDueC/aMGdqAHmPWze52uvvfE6yKGKjieVYysWs8/RYFUBvs6IwGeQl/Flf2Luu/GDqyO2nZ4n6DTDiOXYptw==", + "dependencies": { + "@getpara/shared": "1.10.0", + "axios": "^1.8.4", + "libphonenumber-js": "^1.11.7" + } + }, + "node_modules/@getpara/react-common/node_modules/@getpara/web-sdk": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@getpara/web-sdk/-/web-sdk-2.9.0.tgz", + "integrity": "sha512-2FR9P6ArAUrQdKWiu3FvYSYaWN7/EJPxjA8awh20KOhV1md/AV7atdhiXpXhXfkXYiDYVPIGe6smVP9wVjmOsQ==", + "dependencies": { + "@getpara/core-sdk": "2.9.0", + "@getpara/user-management-client": "2.9.0", + "base64url": "^3.0.1", + "buffer": "6.0.3", + "cbor-web": "^9.0.2", + "node-forge": "1.3.1", + "ua-parser-js": "^2.0.2" + }, + "peerDependencies": { + "@farcaster/miniapp-sdk": "^0.1.2" + }, + "peerDependenciesMeta": { + "@farcaster/miniapp-sdk": { + "optional": true + } } }, - "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node_modules/@getpara/react-components": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@getpara/react-components/-/react-components-2.9.0.tgz", + "integrity": "sha512-4GIp2PTiPvMYmhxYC6npCrln74fjlvzbyefwwP4wsDzwhUjOKOfWKjvOs3W6VKoMjY/AeZ5f2MHNAdLSsJLL1g==", + "dependencies": { + "@getpara/core-components": "2.9.0" } }, - "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@getpara/react-sdk-lite": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@getpara/react-sdk-lite/-/react-sdk-lite-2.9.0.tgz", + "integrity": "sha512-cWld5fO1TWuISclRlVZ/Kiv2Jfg3NbFsu7LOvavekyZVq+37DdsGgnhhCS8oe5rkkx/Ln3lBydlDB9hKCVs5kg==", "dependencies": { - "@types/json-schema": "^7.0.15" + "@getpara/react-common": "2.9.0", + "@getpara/react-components": "2.9.0", + "@getpara/web-sdk": "2.9.0", + "date-fns": "^3.6.0", + "framer-motion": "^11.3.31", + "libphonenumber-js": "^1.11.7", + "styled-components": "^6.1.8", + "zustand": "^4.5.2", + "zustand-sync-tabs": "^0.2.2" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "bin": { + "setup-para": "dist/cli/cli.mjs" + }, + "peerDependencies": { + "@tanstack/react-query": ">=5.0.0", + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@getpara/react-sdk-lite/node_modules/@getpara/core-sdk": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@getpara/core-sdk/-/core-sdk-2.9.0.tgz", + "integrity": "sha512-PMjIVs28cBxEph7mm4dkU2Fhcoeah+8DXxUW3C7iuTqiz04EwWMtq0IvQJGW7oCAPuJ7oRL4vP2rpBgUWIgg0w==", + "dependencies": { + "@celo/utils": "^8.0.2", + "@cosmjs/encoding": "^0.32.4", + "@ethereumjs/util": "^9.1.0", + "@getpara/user-management-client": "2.9.0", + "@noble/hashes": "^1.5.0", + "base64url": "^3.0.1", + "libphonenumber-js": "^1.11.7", + "node-forge": "^1.3.1", + "uuid": "^11.1.0" + } + }, + "node_modules/@getpara/react-sdk-lite/node_modules/@getpara/shared": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@getpara/shared/-/shared-1.10.0.tgz", + "integrity": "sha512-xSBzCrQEbm+p32aoIjZcKL+TrfNRRYvuwGQrzvEMLB+vUxvkPMWz2n1uifRKzIVb/1MtIX4lDv5WiNe8xqFS9A==" + }, + "node_modules/@getpara/react-sdk-lite/node_modules/@getpara/user-management-client": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@getpara/user-management-client/-/user-management-client-2.9.0.tgz", + "integrity": "sha512-/DDueC/aMGdqAHmPWze52uvvfE6yKGKjieVYysWs8/RYFUBvs6IwGeQl/Flf2Luu/GDqyO2nZ4n6DTDiOXYptw==", + "dependencies": { + "@getpara/shared": "1.10.0", + "axios": "^1.8.4", + "libphonenumber-js": "^1.11.7" + } + }, + "node_modules/@getpara/react-sdk-lite/node_modules/@getpara/web-sdk": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@getpara/web-sdk/-/web-sdk-2.9.0.tgz", + "integrity": "sha512-2FR9P6ArAUrQdKWiu3FvYSYaWN7/EJPxjA8awh20KOhV1md/AV7atdhiXpXhXfkXYiDYVPIGe6smVP9wVjmOsQ==", + "dependencies": { + "@getpara/core-sdk": "2.9.0", + "@getpara/user-management-client": "2.9.0", + "base64url": "^3.0.1", + "buffer": "6.0.3", + "cbor-web": "^9.0.2", + "node-forge": "1.3.1", + "ua-parser-js": "^2.0.2" + }, + "peerDependencies": { + "@farcaster/miniapp-sdk": "^0.1.2" + }, + "peerDependenciesMeta": { + "@farcaster/miniapp-sdk": { + "optional": true + } } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, + "node_modules/@getpara/react-sdk-lite/node_modules/framer-motion": { + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", "license": "MIT", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", + "tslib": "^2.4.0" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } } }, - "node_modules/@eslint/js": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", - "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", - "dev": true, + "node_modules/@getpara/react-sdk-lite/node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" + "dependencies": { + "motion-utils": "^11.18.1" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node_modules/@getpara/react-sdk-lite/node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", + "license": "MIT" + }, + "node_modules/@getpara/shared": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@getpara/shared/-/shared-1.8.0.tgz", + "integrity": "sha512-zDZRtTfmrNvuve/99axHpgEl0HfwmESS37BfWk2yCCw397N6wThSKEFKJN/mpJCMVKCPcZjRrKtxq1iTjhmAEA==", + "peer": true + }, + "node_modules/@getpara/user-management-client": { + "version": "2.0.0-alpha.73", + "resolved": "https://registry.npmjs.org/@getpara/user-management-client/-/user-management-client-2.0.0-alpha.73.tgz", + "integrity": "sha512-dcvWinImx58/rLF+eGvear9SqJ/hfD2MR0enzdVoX3e9aIVkcjDBt+3G2rK/Jo6kmgvY6TafMOJQ3/Eh/rfw8w==", + "peer": true, + "dependencies": { + "@getpara/shared": "1.8.0", + "axios": "^1.8.4", + "libphonenumber-js": "^1.11.7" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@getpara/web-sdk": { + "version": "2.0.0-alpha.73", + "resolved": "https://registry.npmjs.org/@getpara/web-sdk/-/web-sdk-2.0.0-alpha.73.tgz", + "integrity": "sha512-EIBWnFDjpwKwXBVVJnAMuQinw/r/RuH20XtKZ7I9ii4bfTV7xVIsOdFyEa641rOcz6KmV7KmWjiswz3zVJczaw==", + "peer": true, "dependencies": { - "@eslint/core": "^0.15.1", - "levn": "^0.4.1" + "@getpara/core-sdk": "2.0.0-alpha.73", + "@getpara/user-management-client": "2.0.0-alpha.73", + "base64url": "^3.0.1", + "buffer": "6.0.3", + "cbor-web": "^9.0.2", + "node-forge": "1.3.1", + "ua-parser-js": "^2.0.2" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "peerDependencies": { + "@farcaster/miniapp-sdk": "^0.1.2" + }, + "peerDependenciesMeta": { + "@farcaster/miniapp-sdk": { + "optional": true + } } }, "node_modules/@humanfs/core": { @@ -1308,9 +1664,9 @@ } }, "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", + "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", "license": "MIT", "dependencies": { "@isaacs/balanced-match": "^4.0.1" @@ -1346,41 +1702,235 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@miden-sdk/miden-para": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@miden-sdk/miden-para/-/miden-para-0.13.1.tgz", + "integrity": "sha512-rvFKIy/tmbd5+8KfiZLXBxcF7/P4SPA3rgQYTqVwyeDbkJG3+mysaTfXNNABdc9v03wsCnYkaTa0lE718GOcRA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.0.1" + }, + "peerDependencies": { + "@getpara/web-sdk": "2.0.0-alpha.73", + "@miden-sdk/miden-sdk": "^0.13.0" + } + }, + "node_modules/@miden-sdk/miden-para/node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@miden-sdk/miden-sdk": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@miden-sdk/miden-sdk/-/miden-sdk-0.13.0.tgz", + "integrity": "sha512-N0qUCZW9Dvk3Oqj37IrGmm0b0v3Nq5qHsX3BtQIzZIwDXKXKPBxy/0lO40oCwDtwI8AfriZQyMLbJR81Fo4Vpg==", + "dependencies": { + "@rollup/plugin-typescript": "^12.3.0", + "dexie": "^4.0.1", + "glob": "^11.0.0" + } + }, + "node_modules/@miden-sdk/miden-sdk/node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@miden-sdk/miden-sdk/node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@miden-sdk/miden-sdk/node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@miden-sdk/miden-sdk/node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@miden-sdk/miden-sdk/node_modules/minimatch": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", + "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@miden-sdk/miden-sdk/node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@miden-sdk/react": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@miden-sdk/react/-/react-0.13.2.tgz", + "integrity": "sha512-78i3/5YUUwitqvJA02HVXZ61RXEOyaiRsm1agVUtOblOmazuIgMEW4P3gdlX9YUiGr06l9O+Rw1ol5FZCOJTXQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "zustand": "^5.0.0" + }, + "peerDependencies": { + "@miden-sdk/miden-sdk": "^0.13.0", + "react": ">=18.0.0" + } + }, + "node_modules/@miden-sdk/react/node_modules/zustand": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.11.tgz", + "integrity": "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + }, + "node_modules/@miden-sdk/use-miden-para-react": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@miden-sdk/use-miden-para-react/-/use-miden-para-react-0.13.1.tgz", + "integrity": "sha512-b/sbG8CXOwedYFUCL/zr2fSugzEdt9mTYSl/2Cf+EUAOwp2JMxDLV5BfdZU9/soV7BIyBdaYXOlNkr+/fxshcw==", "license": "MIT", "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" + "node": ">=18" + }, + "peerDependencies": { + "@getpara/react-sdk-lite": "^2.2.0", + "@getpara/web-sdk": "2.0.0-alpha.73", + "@miden-sdk/miden-para": "^0.13.0", + "@miden-sdk/miden-sdk": "^0.13.0", + "@miden-sdk/react": "^0.13.1", + "@tanstack/react-query": "^5.0.0", + "react": "^18.0.0 || ^19.0.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "node_modules/@moonpay/moonpay-react": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@moonpay/moonpay-react/-/moonpay-react-1.10.6.tgz", + "integrity": "sha512-o+fa3FDKITbOTlkKToT49865ZGKkoGT1/eI0XJnYh9KSNMz3oo2PuwDKJHqLLZ5qE642ZAebksWdEElRySD43Q==", "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "peerDependencies": { + "react": ">=16" } }, "node_modules/@napi-rs/wasm-runtime": { @@ -1540,6 +2090,66 @@ "node": ">= 10" } }, + "node_modules/@noble/ciphers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.1.3.tgz", + "integrity": "sha512-Ygv6WnWJHLLiW4fnNDC1z+i13bud+enXOFRBlpxI+NJliPWx5wdR+oWlTjLuBPTqjUjtHXtjkU6w3kuuH6upZA==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", + "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.3" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1585,6 +2195,41 @@ "node": ">=12.4.0" } }, + "node_modules/@openzeppelin/miden-multisig-client": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/miden-multisig-client/-/miden-multisig-client-0.13.0.tgz", + "integrity": "sha512-b0wxXCM6iSWlKLKb0QzG15z4+UeqKGv4920Gvk6jn1ofeth1uy8Q8pTdDICe/L1UvwUsgqLhicEADUKkB+KOiw==", + "license": "MIT", + "dependencies": { + "@miden-sdk/miden-sdk": "^0.13.0", + "@noble/hashes": "^2.0.1", + "@openzeppelin/psm-client": "^0.13.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@openzeppelin/miden-multisig-client/node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@openzeppelin/psm-client": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/psm-client/-/psm-client-0.13.0.tgz", + "integrity": "sha512-pf/b5CpWfVDbYBSXCDxyuRgbgrKNCB8Y/Bw9U2vW3DteUmfGdsCeCX+PprwVbTvNo1thfja1NzbEfkAvO9XLIw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1595,6 +2240,18 @@ "node": ">=14" } }, + "node_modules/@ramp-network/ramp-instant-sdk": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@ramp-network/ramp-instant-sdk/-/ramp-instant-sdk-4.0.8.tgz", + "integrity": "sha512-dYB94I7mxWcqUY/SaUHtCrBsjk/vhRw9wW1Bm0cIpPp6TT9FOXAYMI+NJQWrHiJeh0i/t9sT63ppmQGDUeu7bQ==", + "license": "MIT", + "dependencies": { + "body-scroll-lock": "^3.1.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@reduxjs/toolkit": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.8.2.tgz", @@ -1621,80 +2278,256 @@ } } }, - "node_modules/@rollup/plugin-typescript": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.3.0.tgz", - "integrity": "sha512-7DP0/p7y3t67+NabT9f8oTBFE6gGkto4SA6Np2oudYmZE/m1dt8RB0SjL1msMxFpLo631qjRCcBlAbq1ml/Big==", + "node_modules/@rollup/plugin-typescript": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.3.0.tgz", + "integrity": "sha512-7DP0/p7y3t67+NabT9f8oTBFE6gGkto4SA6Np2oudYmZE/m1dt8RB0SjL1msMxFpLo631qjRCcBlAbq1ml/Big==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0||^3.0.0||^4.0.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "tslib": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz", + "integrity": "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz", + "integrity": "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz", + "integrity": "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz", + "integrity": "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz", + "integrity": "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz", + "integrity": "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz", + "integrity": "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz", + "integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz", + "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^5.1.0", - "resolve": "^1.22.1" + "@noble/hashes": "1.4.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "license": "MIT", "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.14.0||^3.0.0||^4.0.0", - "tslib": "*", - "typescript": ">=3.7.0" + "node": ">= 16" }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - }, - "tslib": { - "optional": true - } + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@rollup/pluginutils": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", - "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", "license": "MIT", "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@rollup/pluginutils/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 16" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz", - "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", - "dev": true, - "license": "MIT" - }, "node_modules/@standard-schema/spec": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", @@ -1707,6 +2540,29 @@ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", "license": "MIT" }, + "node_modules/@stencil/core": { + "version": "4.42.1", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.42.1.tgz", + "integrity": "sha512-KsjkaWnJmjMcsLjTK77FJV/8OK9qZ4f83/sXzYsjkUF21pqpNrDUkY6/+rnVFWnVfoYMpNdjWfGyJem4VlcMXw==", + "license": "MIT", + "bin": { + "stencil": "bin/stencil" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=7.10.0" + }, + "optionalDependencies": { + "@rollup/rollup-darwin-arm64": "4.34.9", + "@rollup/rollup-darwin-x64": "4.34.9", + "@rollup/rollup-linux-arm64-gnu": "4.34.9", + "@rollup/rollup-linux-arm64-musl": "4.34.9", + "@rollup/rollup-linux-x64-gnu": "4.34.9", + "@rollup/rollup-linux-x64-musl": "4.34.9", + "@rollup/rollup-win32-arm64-msvc": "4.34.9", + "@rollup/rollup-win32-x64-msvc": "4.34.9" + } + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -1716,6 +2572,32 @@ "tslib": "^2.8.0" } }, + "node_modules/@tanstack/query-core": { + "version": "5.90.20", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz", + "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.21", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.21.tgz", + "integrity": "sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.20" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", @@ -1727,6 +2609,15 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -1775,7 +2666,6 @@ "version": "20.19.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -1785,6 +2675,7 @@ "version": "19.1.9", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz", "integrity": "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==", + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -1794,11 +2685,18 @@ "version": "19.1.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz", "integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==", + "dev": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" } }, + "node_modules/@types/stylis": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.7.tgz", + "integrity": "sha512-VgDNokpBoKF+wrdvhAAfS55OMQpL6QRglwTwNC3kIgBrzZxA4WsFj+2eLfEA/uMUDzBcEhYmjSbwQakn/i3ajA==", + "license": "MIT" + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", @@ -2896,6 +3794,12 @@ "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/autoprefixer": { "version": "10.4.21", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", @@ -2960,6 +3864,17 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -2976,6 +3891,35 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/baseline-browser-mapping": { "version": "2.8.32", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", @@ -2986,6 +3930,21 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -2998,6 +3957,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bn.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "license": "MIT" + }, + "node_modules/body-scroll-lock": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz", + "integrity": "sha512-Yi1Xaml0EvNA0OYWxXiYNqY24AfWkbA6w5vxE7GWxtKfzIbZM+Qw+aSmkgsbWzbHiy/RCSkUZBplVxTA+E4jJg==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -3021,6 +3992,12 @@ "node": ">=8" } }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "license": "MIT" + }, "node_modules/browserslist": { "version": "4.28.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", @@ -3055,6 +4032,30 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3086,7 +4087,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3123,6 +4123,15 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -3132,6 +4141,15 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001757", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz", @@ -3152,6 +4170,24 @@ ], "license": "CC-BY-4.0" }, + "node_modules/case": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz", + "integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==", + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cbor-web": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cbor-web/-/cbor-web-9.0.2.tgz", + "integrity": "sha512-N6gU2GsJS8RR5gy1d9wQcSPgn9FGJFY7KNvdDRlwHfz6kCxrQr2TDnrjXHmr6TFSl6Fd0FC4zRnityEldjRGvQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3222,6 +4258,72 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -3236,6 +4338,15 @@ "node": ">=12.5.0" } }, + "node_modules/color-blend": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/color-blend/-/color-blend-4.0.0.tgz", + "integrity": "sha512-fYODTHhI/NG+B5GnzvuL3kiFrK/UnkUezWFTgEPBTY5V+kpyfAn95Vn9sJeeCX6omrCOdxnqCL3CvH+6sXtIbw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3259,10 +4370,28 @@ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "license": "MIT", - "optional": true, + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color2k": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", + "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, "node_modules/commander": { @@ -3319,6 +4448,26 @@ "node": ">= 8" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "license": "MIT", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -3332,9 +4481,9 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -3398,6 +4547,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -3416,6 +4575,15 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3459,6 +4627,35 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-europe-js": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/detect-europe-js/-/detect-europe-js-0.1.2.tgz", + "integrity": "sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", @@ -3481,6 +4678,12 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "license": "Apache-2.0" }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -3504,7 +4707,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -3528,6 +4730,27 @@ "dev": true, "license": "ISC" }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -3622,7 +4845,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3632,7 +4854,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3678,7 +4899,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -3691,7 +4911,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3734,47 +4953,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", - "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.9", - "@esbuild/android-arm": "0.25.9", - "@esbuild/android-arm64": "0.25.9", - "@esbuild/android-x64": "0.25.9", - "@esbuild/darwin-arm64": "0.25.9", - "@esbuild/darwin-x64": "0.25.9", - "@esbuild/freebsd-arm64": "0.25.9", - "@esbuild/freebsd-x64": "0.25.9", - "@esbuild/linux-arm": "0.25.9", - "@esbuild/linux-arm64": "0.25.9", - "@esbuild/linux-ia32": "0.25.9", - "@esbuild/linux-loong64": "0.25.9", - "@esbuild/linux-mips64el": "0.25.9", - "@esbuild/linux-ppc64": "0.25.9", - "@esbuild/linux-riscv64": "0.25.9", - "@esbuild/linux-s390x": "0.25.9", - "@esbuild/linux-x64": "0.25.9", - "@esbuild/netbsd-arm64": "0.25.9", - "@esbuild/netbsd-x64": "0.25.9", - "@esbuild/openbsd-arm64": "0.25.9", - "@esbuild/openbsd-x64": "0.25.9", - "@esbuild/openharmony-arm64": "0.25.9", - "@esbuild/sunos-x64": "0.25.9", - "@esbuild/win32-arm64": "0.25.9", - "@esbuild/win32-ia32": "0.25.9", - "@esbuild/win32-x64": "0.25.9" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -4230,6 +5408,71 @@ "node": ">=0.10.0" } }, + "node_modules/ethereum-bloom-filters": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.2.0.tgz", + "integrity": "sha512-28hyiE7HVsWubqhpVLVmZXFd4ITeHi+BUu05o9isf0GUpMtzBUi+8/gFrGaGYzvGAJQmJ3JKj77Mk9G98T84rA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.4.0" + } + }, + "node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "license": "MIT", + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", + "license": "MIT", + "dependencies": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/ethjs-unit/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "license": "MIT" + }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", @@ -4387,6 +5630,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -4419,6 +5682,28 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fp-ts": { + "version": "2.16.9", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.9.tgz", + "integrity": "sha512-+I2+FnVB+tVaxcYyQkHUq7ZdKScaBlX53A41mxQtpIccsfyv8PzdzP7fzp2AY832T4aoK6UZ5WRX/ebGd8uZuQ==", + "license": "MIT" + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -4514,11 +5799,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -4543,7 +5836,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -4682,7 +5974,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4706,6 +5997,12 @@ "dev": true, "license": "MIT" }, + "node_modules/gsap": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.14.2.tgz", + "integrity": "sha512-P8/mMxVLU7o4+55+1TCnQrPmgjPKnwkzkXOK1asnR9Jg2lna4tEY5qBJjMmAaOBDDZWtlRjBXjLa0w53G/uBLA==", + "license": "Standard 'no charge' license: https://gsap.com/standard-license." + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -4762,7 +6059,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4775,7 +6071,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -4787,6 +6082,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -4799,6 +6104,37 @@ "node": ">= 0.4" } }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4846,6 +6182,18 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inputmask": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/inputmask/-/inputmask-5.0.9.tgz", + "integrity": "sha512-s0lUfqcEbel+EQXtehXqwCJGShutgieOaIImFKC/r4reYNvX3foyrChl6LOEvaEgxEbesePIrw1Zi2jhZaDZbQ==", + "license": "MIT" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -4861,6 +6209,15 @@ "node": ">= 0.4" } }, + "node_modules/io-ts": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.0.1.tgz", + "integrity": "sha512-RezD+WcCfW4VkMkEcQWL/Nmy/nqsWTvTYg7oUmTGzglvSSV2P9h2z1PVeREPFf0GWNzruYleAt1XCMQZSg1xxQ==", + "license": "MIT", + "peerDependencies": { + "fp-ts": "^2.0.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -5089,6 +6446,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "license": "MIT", + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -5189,6 +6556,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-standalone-pwa": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-standalone-pwa/-/is-standalone-pwa-0.1.1.tgz", + "integrity": "sha512-9Cbovsa52vNQCjdXOzeQq5CnCbAcRk05aU62K20WO372NrTv0NxibLFCK6lQ4/iZEFdEA3p3t2VNOn8AJ53F5g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "license": "MIT" + }, "node_modules/is-string": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", @@ -5365,6 +6752,12 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5487,6 +6880,12 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.12.36", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.36.tgz", + "integrity": "sha512-woWhKMAVx1fzzUnMCyOzglgSgf6/AFHLASdOBcchYCyvWSGWt12imw3iu2hdI5d4dGZRsNWAmWiz37sDKUPaRQ==", + "license": "MIT" + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -5566,7 +6965,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5589,6 +6987,12 @@ "node": ">= 8" } }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -5606,9 +7010,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -5617,9 +7019,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -5627,6 +7027,18 @@ "node": ">= 0.6" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "license": "MIT" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5821,6 +7233,15 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -5847,6 +7268,26 @@ "node": ">=0.10.0" } }, + "node_modules/number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "license": "MIT", + "dependencies": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/number-to-bn/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6046,6 +7487,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -6069,7 +7519,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6142,6 +7591,15 @@ "node": ">= 6" } }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -6282,6 +7740,12 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -6292,6 +7756,32 @@ "node": ">=6" } }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode-with-logos": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/qrcode-with-logos/-/qrcode-with-logos-1.1.1.tgz", + "integrity": "sha512-vh0LWvwalp7471ODXd4rcXrbKgRpfwfdrjfb3pdr9+Zz/s27+5clRHFxzh5orTm1rusStfHGMACP7GWVAkzpKQ==", + "license": "MIT", + "dependencies": { + "qrcode": "^1.4.4" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6316,7 +7806,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" @@ -6394,6 +7883,12 @@ "node": ">=8.10.0" } }, + "node_modules/readonly-date": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz", + "integrity": "sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==", + "license": "Apache-2.0" + }, "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", @@ -6453,6 +7948,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -6463,6 +7967,12 @@ "node": ">=0.10.0" } }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, "node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", @@ -6566,7 +8076,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -6704,6 +8213,12 @@ "randombytes": "^2.1.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -6753,6 +8268,12 @@ "node": ">= 0.4" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT" + }, "node_modules/sharp": { "version": "0.34.3", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", @@ -6915,6 +8436,16 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/sonner": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", + "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7187,6 +8718,19 @@ "node": ">=4" } }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "license": "MIT", + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -7200,6 +8744,67 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/styled-components": { + "version": "6.3.9", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.3.9.tgz", + "integrity": "sha512-J72R4ltw0UBVUlEjTzI0gg2STOqlI9JBhQOL4Dxt7aJOnnSesy0qJDn4PYfMCafk9cWOaVg129Pesl5o+DIh0Q==", + "license": "MIT", + "dependencies": { + "@emotion/is-prop-valid": "1.4.0", + "@emotion/unitless": "0.10.0", + "@types/stylis": "4.2.7", + "css-to-react-native": "3.2.0", + "csstype": "3.2.3", + "postcss": "8.4.49", + "shallowequal": "1.1.0", + "stylis": "4.3.6", + "tslib": "2.8.1" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -7223,6 +8828,12 @@ } } }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -7690,6 +9301,57 @@ "node": ">=14.17" } }, + "node_modules/ua-is-frozen": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ua-is-frozen/-/ua-is-frozen-0.1.2.tgz", + "integrity": "sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "license": "MIT" + }, + "node_modules/ua-parser-js": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-2.0.9.tgz", + "integrity": "sha512-OsqGhxyo/wGdLSXMSJxuMGN6H4gDnKz6Fb3IBm4bxZFMnyy0sdf6MN96Ie8tC6z/btdO+Bsy8guxlvLdwT076w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "AGPL-3.0-or-later", + "dependencies": { + "detect-europe-js": "^0.1.2", + "is-standalone-pwa": "^0.1.1", + "ua-is-frozen": "^0.1.2" + }, + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -7713,7 +9375,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/unrs-resolver": { @@ -7801,12 +9462,31 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", + "license": "MIT" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/watchpack": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", @@ -7822,6 +9502,64 @@ "node": ">=10.13.0" } }, + "node_modules/web3-eth-abi": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.10.4.tgz", + "integrity": "sha512-cZ0q65eJIkd/jyOlQPDjr8X4fU6CRL1eWgdLwbWEpo++MPU/2P4PFk5ZLAdye9T5Sdp+MomePPJ/gHjLMj2VfQ==", + "license": "LGPL-3.0", + "dependencies": { + "@ethersproject/abi": "^5.6.3", + "web3-utils": "1.10.4" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.4.tgz", + "integrity": "sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==", + "license": "LGPL-3.0", + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereum-cryptography": "^2.1.2", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils/node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/web3-utils/node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "license": "MPL-2.0", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/webpack": { "version": "5.103.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz", @@ -7991,6 +9729,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, "node_modules/which-typed-array": { "version": "1.1.19", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", @@ -8111,6 +9855,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, "node_modules/yaml": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", @@ -8123,6 +9873,134 @@ "node": ">= 14.6" } }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/yargs/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -8135,6 +10013,47 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/zustand-sync-tabs": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/zustand-sync-tabs/-/zustand-sync-tabs-0.2.3.tgz", + "integrity": "sha512-NtA4g0v6bh34jYzbGVgjsfR6rPjlUc9Oj6QAeKWikxV3UIYEhPo9AEbAk/3X0p6H1o/waBKqZ/nLfRuz7OH4rA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/mayank1513" + }, + "peerDependencies": { + "zustand": ">=3.0.0 <6.0.0" + } } } } diff --git a/bin/coordinator-frontend/package.json b/bin/coordinator-frontend/package.json index c122cb5..60bd2d5 100644 --- a/bin/coordinator-frontend/package.json +++ b/bin/coordinator-frontend/package.json @@ -9,16 +9,22 @@ "lint": "next lint" }, "dependencies": { - "@demox-labs/miden-sdk": "^0.12.3", - "@demox-labs/miden-wallet-adapter": "^0.10.0", "@demox-labs/miden-wallet-adapter-base": "^0.10.0", "@demox-labs/miden-wallet-adapter-miden": "^0.10.0", + "@getpara/react-sdk-lite": "^2.2.0", + "@miden-sdk/miden-para": "^0.13.0", + "@miden-sdk/miden-sdk": "^0.13.0", + "@miden-sdk/use-miden-para-react": "^0.13.0", + "@openzeppelin/miden-multisig-client": "^0.13.0", + "@openzeppelin/psm-client": "^0.13.0", "@reduxjs/toolkit": "^2.8.2", + "@tanstack/react-query": "^5.0.0", "framer-motion": "^12.23.12", "next": "15.4.5", "react": "^19.1.1", "react-dom": "^19.1.1", "react-redux": "^9.2.0", + "sonner": "^2.0.7", "tailwind-scrollbar": "^3.0.5" }, "devDependencies": { diff --git a/bin/coordinator-frontend/src/app/dashboard/assets/page.tsx b/bin/coordinator-frontend/src/app/dashboard/assets/page.tsx index 04642e5..2e5abef 100644 --- a/bin/coordinator-frontend/src/app/dashboard/assets/page.tsx +++ b/bin/coordinator-frontend/src/app/dashboard/assets/page.tsx @@ -1,43 +1,42 @@ "use client"; -import React, { useState, useEffect } from "react"; +import React, { useMemo } from "react"; import media from "../../../../public/media"; import Image from "next/image"; import TokenHoldings from "./components/TokenHoldings"; - -import { useFungibleAssets } from "@/hooks/useFungibleAssets"; +import { useMultisig } from "@/contexts/MultisigContext"; // Force dynamic rendering to avoid WASM loading issues during build export const dynamic = 'force-dynamic'; const Assets = () => { - const { fungibleAssets, isLoading } = useFungibleAssets(); - const [fungibleAssetsCount, setFungibleAssetsCount] = useState(0); - const [totalBalance, setTotalBalance] = useState(0); - const [fungibleAssetsWithPercentage, setFungibleAssetsWithPercentage] = useState>([]); + const { detectedConfig, syncingState } = useMultisig(); + + const vaultBalances = detectedConfig?.vaultBalances ?? []; - useEffect(() => { - if (fungibleAssets.length > 0) { - // Calculate total balance for percentage calculation - const totalBalanceBigInt = fungibleAssets.reduce((sum, asset) => sum + BigInt(asset.balance), BigInt(0)); - - // Calculate total balance in readable format (divided by 1000000) - const totalBalanceDisplay = Number(totalBalanceBigInt) / 1000000; - setTotalBalance(totalBalanceDisplay); - - // Add percentage to each asset - const assetsWithPercentage = fungibleAssets.map(asset => { - const balance = BigInt(asset.balance); - const percentage = totalBalanceBigInt > 0 ? Number((balance * BigInt(100)) / totalBalanceBigInt) : 0; - return { ...asset, percentage }; - }); - - setFungibleAssetsWithPercentage(assetsWithPercentage); - setFungibleAssetsCount(fungibleAssets.length); - } else { - setTotalBalance(0); + const { totalBalance, fungibleAssetsWithPercentage } = useMemo(() => { + if (vaultBalances.length === 0) { + return { totalBalance: 0, fungibleAssetsWithPercentage: [] }; } - }, [fungibleAssets]); + + const totalBigInt = vaultBalances.reduce((sum, b) => sum + BigInt(b.amount), BigInt(0)); + const totalDisplay = Number(totalBigInt) / 1000000; + + const withPercentage = vaultBalances.map(b => { + const balance = BigInt(b.amount); + const percentage = totalBigInt > 0n ? Number((balance * 100n) / totalBigInt) : 0; + return { faucetId: b.faucetId, balance: b.amount.toString(), percentage }; + }); + + return { totalBalance: totalDisplay, fungibleAssetsWithPercentage: withPercentage }; + }, [vaultBalances]); + + const fungibleAssets = useMemo(() => { + return vaultBalances.map(b => ({ + faucetId: b.faucetId, + balance: b.amount.toString(), + })); + }, [vaultBalances]); return ( <> @@ -69,7 +68,7 @@ const Assets = () => {
{totalBalance.toFixed(2)}
-
USD
+
{vaultBalances.length} token(s)
{/*This Month Div*/} @@ -86,7 +85,7 @@ const Assets = () => {
- {fungibleAssetsCount} + {vaultBalances.length}
Total
@@ -104,29 +103,23 @@ const Assets = () => {
-
{fungibleAssetsWithPercentage.map((asset, index) => ( - {/* Asset Block */}
- Mid {index + 1} + Token {index + 1}
{asset.percentage}%
- - {/* Vertical Divider (except for last item) */} {index < fungibleAssetsWithPercentage.length - 1 && (
)}
))} - - {/* Show message if no assets */} {fungibleAssetsWithPercentage.length === 0 && (
No tokens found
)} @@ -136,9 +129,8 @@ const Assets = () => {
- -
- + +
); diff --git a/bin/coordinator-frontend/src/app/dashboard/components/MultisigSetup.tsx b/bin/coordinator-frontend/src/app/dashboard/components/MultisigSetup.tsx deleted file mode 100644 index 8cd14fc..0000000 --- a/bin/coordinator-frontend/src/app/dashboard/components/MultisigSetup.tsx +++ /dev/null @@ -1,203 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import { MidenWebClientHandle } from '../../../../lib/miden-client'; -import { SecretKey } from '@demox-labs/miden-sdk'; - -interface MultisigSetupProps { - midenHandle: MidenWebClientHandle | null; -} - -interface MultisigSetupResult { - accountId?: string; - status?: string; - [key: string]: unknown; -} - -export default function MultisigSetup({ midenHandle }: MultisigSetupProps) { - const [publicKeys, setPublicKeys] = useState(['', '', '']); - const [threshold, setThreshold] = useState(2); - const [isLoading, setIsLoading] = useState(false); - const [result, setResult] = useState(null); - const [error, setError] = useState(''); - - const addPublicKey = () => { - setPublicKeys([...publicKeys, '']); - }; - - const removePublicKey = (index: number) => { - if (publicKeys.length > 1) { - const newKeys = publicKeys.filter((_, i) => i !== index); - setPublicKeys(newKeys); - } - }; - - const updatePublicKey = (index: number, value: string) => { - const newKeys = [...publicKeys]; - newKeys[index] = value; - setPublicKeys(newKeys); - }; - - const generateRandomPublicKey = (index: number) => { - try { - const secretKey = SecretKey.withRng(); - const publicKey = secretKey.publicKey(); - const serializedBytes = publicKey.serialize(); - const publicKeyString = Array.from(serializedBytes) - .map(byte => byte.toString(16).padStart(2, '0')) - .join(''); - updatePublicKey(index, publicKeyString); - setError(''); - } catch (err) { - console.error('Error generating random public key:', err); - setError('Failed to generate random public key. Please try again.'); - } - }; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - if (!midenHandle) { - setError('Miden client not initialized'); - return; - } - - // Filter out empty public keys - const validPublicKeys = publicKeys.filter(key => key.trim() !== ''); - - if (validPublicKeys.length === 0) { - setError('Please provide at least one public key'); - return; - } - - if (threshold > validPublicKeys.length) { - setError('Threshold cannot be greater than the number of public keys'); - return; - } - - setIsLoading(true); - setError(''); - setResult(null); - - try { - const webClient = midenHandle.getWebClient(); - if (!webClient) { - throw new Error('Web client not available'); - } - - const publicKeyObjects = validPublicKeys.map(keyString => { - try { - const secretKey = SecretKey.withRng(); - return secretKey.publicKey(); - } catch (_err) { - throw new Error(`Invalid public key format: ${keyString}`); - } - }); - - - const setupResult = webClient.setupAccount(publicKeyObjects, threshold); - - setResult(setupResult); - } catch (err) { - console.error('Error setting up multisig account:', err); - setError(err instanceof Error ? err.message : 'Failed to setup multisig account'); - } finally { - setIsLoading(false); - } - }; - - return ( -
-

Multisig Account Setup

- -
- {/* Public Keys Section */} -
- -
- {publicKeys.map((key, index) => ( -
- updatePublicKey(index, e.target.value)} - placeholder={`Public Key ${index + 1}`} - className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" - /> - - {publicKeys.length > 1 && ( - - )} -
- ))} -
- -
- - {/* Threshold Section */} -
- - setThreshold(parseInt(e.target.value) || 1)} - className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" - /> -

- Must be between 1 and {publicKeys.length} -

-
- - {/* Submit Button */} - -
- - {/* Error Display */} - {error && ( -
- Error: {String(error)} -
- )} - - {/* Success Result */} - {result && ( -
- Success! Multisig account created successfully. -
-            {JSON.stringify(result, null, 2)}
-          
-
- )} -
- ); -} diff --git a/bin/coordinator-frontend/src/app/dashboard/components/PendingActions.tsx b/bin/coordinator-frontend/src/app/dashboard/components/PendingActions.tsx index e546b14..51e0f35 100644 --- a/bin/coordinator-frontend/src/app/dashboard/components/PendingActions.tsx +++ b/bin/coordinator-frontend/src/app/dashboard/components/PendingActions.tsx @@ -1,293 +1,31 @@ "use client"; -import React, { useState, useEffect } from "react"; +import React, { useState, useMemo } from "react"; import Image from "next/image"; import { useRouter } from "next/navigation"; import media from "../../../../public/media"; -import { useAppSelector, useAppDispatch } from "@/store/hooks"; -import PendingTransactionDetails from "@/interactions/PendingTransactionDetails"; +import { useMultisig } from "@/contexts/MultisigContext"; +import { toast } from "sonner"; import { AnimatePresence, motion } from "framer-motion"; -import { TransactionRequest, TransactionSummary, SigningInputs, NoteFile } from "@demox-labs/miden-sdk"; -import { useMidenClient } from "../../../contexts/MidenClientContext"; -import { fetchPendingTransactions, fetchConfirmedTransactions } from "../../../services/transactionApi"; -import { addSignatureThunk } from "../../../services/signatureApi"; -import { useWallet } from "@demox-labs/miden-wallet-adapter"; -import { PendingActionsProps, DecodedTransaction, WebClient } from "@/types"; - -const getSendTransactionAmount = ( - txRequestBase64: string | undefined, - transactionType: "sent" | "received" | null -): number => { - if ( - transactionType !== "sent" || - !txRequestBase64 || - typeof txRequestBase64 !== "string" || - txRequestBase64.trim() === "" - ) { - return 0; - } - - try { - const serializedTxRequest = Uint8Array.fromBase64(txRequestBase64); - const txRequest = TransactionRequest.deserialize(serializedTxRequest); - const expectedOutputOwnNotes = txRequest.expectedOutputOwnNotes(); - - if (expectedOutputOwnNotes.length > 0) { - const firstNote = expectedOutputOwnNotes[0]; - const assets = firstNote.assets(); - const fungibleAssets = assets.fungibleAssets(); - - if (fungibleAssets.length > 0) { - const amount = fungibleAssets[0].amount(); - return Number(amount) / 1000000; - } - } - - return 0; - } catch (error) { - console.error("Error extracting transaction amount:", error); - return 0; - } -}; - -const getReceiveTransactionAmount = async (noteId: string, noteIdFileBytes: string, webClient: WebClient | null): Promise => { - try { - if (!noteId || typeof noteId !== "string" || noteId.trim() === "") { - return 0; - } - - if (!webClient) { - console.error("WebClient not available"); - return 0; - } - - let inputNoteRecord = await webClient.getInputNote(noteId); - if (!inputNoteRecord) { - if (noteIdFileBytes) { - const noteBytes = Uint8Array.fromBase64(noteIdFileBytes); - const noteFile = NoteFile.deserialize(noteBytes); - await webClient.importNoteFile(noteFile); - inputNoteRecord = await webClient.getInputNote(noteId); - } else { - console.error("No note file bytes to import"); - return 0; - } - } - - if (inputNoteRecord) { - const details = inputNoteRecord.details(); - const assets = details.assets(); - const fungibleAssets = assets.fungibleAssets(); - - if (fungibleAssets.length > 0) { - const amount = fungibleAssets[0].amount(); - return Number(amount) / 1000000; - } - } - - return 0; - } catch (error) { - console.error("Error extracting amount from note:", error); - return 0; - } -}; +import { PendingActionsProps } from "@/types"; +import { getEffectiveThreshold } from "@/lib/procedures"; const PendingActions: React.FC = ({ threshold, fixedHeight = false }) => { const router = useRouter(); - const { wallet, address, connected, signBytes } = useWallet(); - const { handle, isInitialized } = useMidenClient(); - const dispatch = useAppDispatch(); - const { pendingTransactions, loading: transactionsLoading } = useAppSelector( - (state) => state.transaction - ); - - const webClient = handle?.getWebClient(); - const [decodedPendingTransactions, setDecodedPendingTransactions] = useState([]); - const [initialLoad, setInitialLoad] = useState(true); - const [hasLoadedOnce, setHasLoadedOnce] = useState(false); - - useEffect(() => { - if (transactionsLoading) { - setHasLoadedOnce(true); - } - if (!transactionsLoading && hasLoadedOnce) { - setInitialLoad(false); - } - }, [transactionsLoading, hasLoadedOnce]); + const { + proposals, + detectedConfig, + handleSignProposal, + handleExecuteProposal, + signingProposal, + executingProposal, + syncingState, + error: contextError, + } = useMultisig(); - const handleSign = async (txReqFromApi: string, txId: string) => { - if (!wallet || !address || !connected) { - setShowWalletErrorModal(true); - setTimeout(() => { - setShowWalletErrorModal(false); - }, 3000); - return; - } - - if (!signBytes) { - showNotification("error", "Sign bytes functionality not available"); - return; - } - - if (!txReqFromApi?.trim()) { - showNotification("error", "Please provide a transaction request to sign"); - return; - } - - setSigningTransactionId(txId); - try { - const txReqBytes = new Uint8Array( - atob(txReqFromApi) - .split('') - .map(char => char.charCodeAt(0)) - ); - const transactionSummary = TransactionSummary.deserialize(txReqBytes); - const signingInputs = SigningInputs.newTransactionSummary(transactionSummary); - let signature: Uint8Array; - try { - signature = await signBytes(signingInputs.serialize(), "signingInputs"); - } catch (signError) { - console.error("Signature creation failed:", signError); - throw new Error( - `Signature failed: ${signError instanceof Error ? signError.message : "Unknown error" - }` - ); - } - - const signatureBase64 = btoa(String.fromCharCode(...signature)); - - try { - const signatureData = { - tx_id: txId, - approver: address, - signature: signatureBase64, - }; - - await dispatch( - addSignatureThunk({ - txId: txId, - signatureData, - }) - ).unwrap(); - - if (handle && isInitialized) { - try { - await handle.syncState(); - } catch (syncError) { - console.error("Error syncing state after signature:", syncError); - } - } - - await refreshAllTransactions(); - - showNotification("success", "Transaction Signed Successfully!"); - } catch (signatureError) { - console.error("Error submitting signature:", signatureError); - showNotification( - "error", - "Failed to submit signature. Please try again." - ); - } - } catch (error) { - console.error("Signing failed:", error); - showNotification("error", "Signing failed. Please try again."); - } finally { - setSigningTransactionId(null); - } - }; - - const getTransactionType = ( - txbz: string | undefined - ): "sent" | "received" | null => { - if (!txbz || typeof txbz !== "string" || txbz.trim() === "") { - return null; - } - - try { - // Decode base64 to get the transaction request - const serializedTXBZ = Uint8Array.fromBase64(txbz); - const deserializedTXBZ = TransactionRequest.deserialize(serializedTXBZ); - const expectedOutputOwnNotes = deserializedTXBZ.expectedOutputOwnNotes(); - - return expectedOutputOwnNotes.length === 0 ? "received" : "sent"; - } catch (error) { - console.error("Error processing tx_bz:", error); - return null; - } - }; - - useEffect(() => { - const decodePendingTransactions = async () => { - if (!pendingTransactions || pendingTransactions.length === 0) { - setDecodedPendingTransactions([]); - return; - } - - try { - const decoded: DecodedTransaction[] = []; - - for (let index = 0; index < pendingTransactions.length; index++) { - const tx = pendingTransactions[index]; - - const transactionType = getTransactionType(tx.tx_request); - const txId = tx.id; - - let amount = 0; - - if (transactionType === "sent") { - amount = getSendTransactionAmount(tx.tx_request, transactionType); - } else if (transactionType === "received" && tx.input_note_ids && tx.input_note_ids.length > 0 && webClient) { - let totalAmount = 0; - for (const inputNote of tx.input_note_ids) { - const noteAmount = await getReceiveTransactionAmount( - inputNote.note_id, - inputNote.note_id_file_bytes, - webClient - ); - totalAmount += noteAmount; - } - amount = totalAmount; - } - - if (amount === 0 && transactionType) { - console.warn(`Transaction ${index} has 0 amount! Type: ${transactionType}`); - } - - decoded.push({ - id: txId, - status: tx.status, - signature_count: tx.signature_count || 0, - type: transactionType === "sent" ? "send" : transactionType === "received" ? "receive" : "transfer", - transactionType: transactionType, - recipient: "", // Not available for pending transactions - amount: amount, - currency: "MIDEN", - priority: "normal", - memo: "", - timestamp: null, - created_at: tx.created_at || "", - tx_request: tx.tx_request, - tx_summary: tx.tx_summary, - } as any); - } - - setDecodedPendingTransactions(decoded); - } catch (error) { - console.error("PendingActions: Error decoding transactions:", error); - setDecodedPendingTransactions([]); - } - }; - - decodePendingTransactions(); - }, [pendingTransactions, webClient]); - - const [isPendingTransactionDetailsOpen, setIsPendingTransactionDetailsOpen] = - useState(false); const [notification, setNotification] = useState<{ type: "success" | "error"; message: string; } | null>(null); - const [signingTransactionId, setSigningTransactionId] = useState(null); - const [showWalletErrorModal, setShowWalletErrorModal] = useState(false); const showNotification = (type: "success" | "error", message: string) => { setNotification({ type, message }); @@ -296,32 +34,34 @@ const PendingActions: React.FC = ({ threshold, fixedHeight }, 3000); }; - const refreshAllTransactions = async () => { + // Filter to only show pending (not yet executed) proposals + const pendingProposals = useMemo(() => { + return proposals.filter(p => p.status.type === 'pending' || p.status.type === 'ready'); + }, [proposals]); + + const effectiveThreshold = threshold ?? detectedConfig?.threshold ?? 0; + + const handleSign = async (proposalId: string) => { try { - const currentWalletId = localStorage.getItem("currentWalletId"); - if (currentWalletId) { - await dispatch( - fetchPendingTransactions({ accountId: currentWalletId }) - ); - await dispatch( - fetchConfirmedTransactions({ accountId: currentWalletId }) - ); - } - } catch (error) { - console.error("Error refreshing transactions:", error); + await handleSignProposal(proposalId); + toast.success("Proposal signed successfully!"); + } catch (err) { + showNotification("error", "Signing failed. Please try again."); } }; - const handleClosePendingTransactionDetails = () => { - setIsPendingTransactionDetailsOpen(false); + const handleExecute = async (proposalId: string) => { + try { + await handleExecuteProposal(proposalId); + } catch (err) { + showNotification("error", "Execution failed. Please try again."); + } }; const handleViewAll = () => { router.push('/dashboard/transactions'); }; - - return (
@@ -339,99 +79,120 @@ const PendingActions: React.FC = ({ threshold, fixedHeight
0 + className={`flex flex-col gap-2 ${pendingProposals.length > 0 ? fixedHeight - ? decodedPendingTransactions.length >= 5 - ? "h-[360px] overflow-hidden" // Fixed height for 5+ items - : "" // Dynamic height for less than 5 items + ? pendingProposals.length >= 5 + ? "h-[360px] overflow-hidden" + : "" : "max-h-[210px] overflow-y-auto scrollbar-thin scrollbar-track-gray-200 scrollbar-thumb-[#CCCCCC] scrollbar-w-[20px]" - : "h-[200px]" // Default height when no items + : "h-[200px]" }`} > - {transactionsLoading || initialLoad ? ( + {syncingState ? (

- Loading transactions... + Syncing proposals...

- ) : decodedPendingTransactions.length > 0 ? ( - decodedPendingTransactions.map((tx, index) => { + ) : pendingProposals.length > 0 ? ( + pendingProposals.map((proposal) => { + const propThreshold = getEffectiveThreshold( + proposal.metadata?.proposalType, + effectiveThreshold, + detectedConfig?.procedureThresholds + ); + const sigCount = proposal.signatures?.length ?? 0; + const isReady = sigCount >= propThreshold; + const isSigning = signingProposal === proposal.id; + const isExecuting = executingProposal === proposal.id; + const isSend = proposal.metadata?.proposalType === 'p2id'; + return (
- {tx.created_at ? new Date(tx.created_at).toISOString().split('T')[0] : new Date().toISOString().split('T')[0]} + {proposal.id.slice(0, 8)}...
-
+
- {tx.transactionType === "sent" - ? `SEND Transaction - ${tx.amount ? tx.amount.toFixed(2) : '0.00'} MIDEN` - : tx.transactionType === "received" - ? `RECEIVE Transaction - ${tx.amount ? tx.amount.toFixed(2) : '0.00'} MIDEN` - : "Transaction"} + {proposal.metadata?.proposalType === 'p2id' ? 'SEND' : + proposal.metadata?.proposalType === 'consume_notes' ? 'RECEIVE' : + proposal.metadata?.proposalType === 'add_signer' ? 'ADD SIGNER' : + proposal.metadata?.proposalType === 'remove_signer' ? 'REMOVE SIGNER' : + proposal.metadata?.proposalType === 'change_threshold' ? 'CHANGE THRESHOLD' : + proposal.metadata?.proposalType === 'switch_psm' ? 'SWITCH PSM' : + (proposal.metadata?.proposalType ?? 'UNKNOWN').toUpperCase()}
-
+
{tx.transactionType
-
- icon -
- {tx.signature_count}/{threshold} signed + {sigCount}/{propThreshold} signed
-
-
- {(threshold || 0) - tx.signature_count} NEEDED -
-
- -
- +
+
+ {isReady ? ( + + ) : ( + + )}
); }) @@ -453,36 +214,14 @@ const PendingActions: React.FC = ({ threshold, fixedHeight

- No pending transactions + No pending proposals

- All transactions have been processed + All proposals have been processed

)}
- - {isPendingTransactionDetailsOpen && ( - - e.stopPropagation()} - initial={{ y: 8, scale: 0.98 }} - animate={{ y: 0, scale: 1 }} - exit={{ y: 8, scale: 0.98 }} - transition={{ duration: 0.25, ease: [0.22, 1, 0.36, 1] }} - > - - - - )} - {notification && ( @@ -502,51 +241,6 @@ const PendingActions: React.FC = ({ threshold, fixedHeight )} - - - {showWalletErrorModal && ( - setShowWalletErrorModal(false)} - > - e.stopPropagation()} - initial={{ scale: 0.95, opacity: 0 }} - animate={{ scale: 1, opacity: 1 }} - exit={{ scale: 0.95, opacity: 0 }} - transition={{ duration: 0.2 }} - className="bg-white rounded-lg shadow-xl p-6 max-w-md w-full mx-4" - > -
-
- - - -
-
-

- No Wallet Connected -

-

- Please connect your wallet to sign transactions. -

-
- -
-
-
- )} -
); }; diff --git a/bin/coordinator-frontend/src/app/dashboard/components/RecentTransactions.tsx b/bin/coordinator-frontend/src/app/dashboard/components/RecentTransactions.tsx index 3ccaa50..b0a8652 100644 --- a/bin/coordinator-frontend/src/app/dashboard/components/RecentTransactions.tsx +++ b/bin/coordinator-frontend/src/app/dashboard/components/RecentTransactions.tsx @@ -1,220 +1,22 @@ "use client"; -import React, { useState, useEffect } from "react"; +import React, { useMemo } from "react"; import Image from "next/image"; import { useRouter } from "next/navigation"; import media from "../../../../public/media"; -import { useAppSelector } from "@/store/hooks"; -import { DecodedTransaction, RecentTransactionsProps } from "@/types"; -import { TransactionRequest, NoteFile } from "@demox-labs/miden-sdk"; -import { useMidenClient } from "../../../contexts/MidenClientContext"; - -const getTransactionType = ( - txRequestBase64: string | undefined -): "sent" | "received" | null => { - if (!txRequestBase64 || typeof txRequestBase64 !== "string" || txRequestBase64.trim() === "") { - return null; - } - - try { - const serializedTxRequest = Uint8Array.fromBase64(txRequestBase64); - const txRequest = TransactionRequest.deserialize(serializedTxRequest); - return txRequest.expectedOutputOwnNotes().length === 0 ? "received" : "sent"; - } catch (error) { - console.error("Error processing txRequest:", error); - return null; - } -}; - -const getSendTransactionAmount = ( - txbz: string | undefined, - transactionType: "sent" | "received" | null -): number => { - if ( - transactionType !== "sent" || - !txbz || - typeof txbz !== "string" || - txbz.trim() === "" - ) { - return 0; - } - - try { - - const serializedTXBZ = Uint8Array.fromBase64(txbz); - const deserializedTXBZ = TransactionRequest.deserialize(serializedTXBZ); - const expectedOutputOwnNotes = deserializedTXBZ.expectedOutputOwnNotes(); - - if (expectedOutputOwnNotes.length > 0) { - const firstNote = expectedOutputOwnNotes[0]; - const assets = firstNote.assets(); - const fungibleAssets = assets.fungibleAssets(); - - if (fungibleAssets.length > 0) { - const amount = fungibleAssets[0].amount(); - return Number(amount) / 1000000; - } - } - - return 0; - } catch (error) { - console.error("Error extracting transaction amount:", error); - return 0; - } -}; - -const getReceiveTransactionAmount = async (noteId: string, noteIdFileBytes: string, webClient: any): Promise => { - try { - if (!noteId || typeof noteId !== "string" || noteId.trim() === "") { - return 0; - } - - if (!webClient) { - console.error("WebClient not available"); - return 0; - } - - let inputNoteRecord = await webClient.getInputNote(noteId); - - // If inputNoteRecord is undefined, import the note file - if (!inputNoteRecord) { - if (noteIdFileBytes) { - const noteBytes = Uint8Array.fromBase64(noteIdFileBytes); - const noteFile = NoteFile.deserialize(noteBytes); - await webClient.importNoteFile(noteFile); - - // Retry getting the note - inputNoteRecord = await webClient.getInputNote(noteId); - } else { - console.error("No note file bytes to import"); - return 0; - } - } - - if (inputNoteRecord) { - const details = inputNoteRecord.details(); - const assets = details.assets(); - const fungibleAssets = assets.fungibleAssets(); - - if (fungibleAssets.length > 0) { - const amount = fungibleAssets[0].amount(); - return Number(amount) / 1000000; - } - } - - return 0; - } catch (error) { - console.error("Error extracting amount from note:", error); - return 0; - } -}; +import { useMultisig } from "@/contexts/MultisigContext"; +import { RecentTransactionsProps } from "@/types"; +import { getEffectiveThreshold } from "@/lib/procedures"; const RecentTransactions: React.FC = ({ threshold, fixedHeight = false }) => { const router = useRouter(); - const { allTransactions, loading: transactionsLoading } = useAppSelector( - (state) => state.transaction - ); - const { handle } = useMidenClient(); - const webClient = handle?.getWebClient(); + const { proposals, detectedConfig, syncingState } = useMultisig(); - const [displayTransactions, setDisplayTransactions] = useState([]); - const [amountsLoading, setAmountsLoading] = useState(false); - const [initialLoad, setInitialLoad] = useState(true); - const [hasLoadedOnce, setHasLoadedOnce] = useState(false); + // Show all proposals (both pending and executed) as recent transactions + const allProposals = useMemo(() => { + return [...proposals].reverse(); // Most recent first + }, [proposals]); - // Track when loading starts and completes - useEffect(() => { - if (transactionsLoading) { - setHasLoadedOnce(true); - } - if (!transactionsLoading && hasLoadedOnce) { - setInitialLoad(false); - } - }, [transactionsLoading, hasLoadedOnce]); - - useEffect(() => { - let isMounted = true; - - const decodeAllTransactions = async () => { - if (!allTransactions || allTransactions.length === 0) { - if (isMounted) { - setDisplayTransactions([]); - } - return; - } - - setAmountsLoading(true); - try { - const decoded: DecodedTransaction[] = []; - - for (let index = 0; index < allTransactions.length; index++) { - if (!isMounted) break; // Stop if component unmounted - - const tx = allTransactions[index]; - const transactionType = getTransactionType(tx.tx_request); - - let amount = 0; - if (transactionType === "sent") { - amount = getSendTransactionAmount(tx.tx_request, transactionType); - } else if (transactionType === "received" && tx.input_note_ids && tx.input_note_ids.length > 0 && webClient) { - // Sum amounts from all input notes - let totalAmount = 0; - for (const inputNote of tx.input_note_ids) { - if (!isMounted) break; // Stop if component unmounted - - const noteAmount = await getReceiveTransactionAmount( - inputNote.note_id, - inputNote.note_id_file_bytes, - webClient - ); - totalAmount += noteAmount; - } - amount = totalAmount; - } else { - amount = 0; - } - - decoded.push({ - id: index, - status: tx.status, - signature_count: tx.signature_count, - type: - transactionType === "sent" - ? "send" - : transactionType === "received" - ? "receive" - : "transfer", - transactionType: transactionType, - recipient: "Unknown", - amount: amount, - currency: "USD", - priority: "normal", - memo: "", - timestamp: null, - created_at: tx.created_at, - }); - } - - if (isMounted) { - setDisplayTransactions(decoded); - } - } catch (error) { - console.error("RecentTransactions: Error decoding transactions:", error); - if (isMounted) { - setDisplayTransactions([]); - } - } finally { - if (isMounted) { - setAmountsLoading(false); - } - } - }; - - decodeAllTransactions(); - - return () => { - isMounted = false; - }; - }, [allTransactions, webClient]); + const effectiveThreshold = threshold ?? detectedConfig?.threshold ?? 0; const handleViewAll = () => { router.push('/dashboard/transactions'); @@ -232,88 +34,90 @@ const RecentTransactions: React.FC = ({ threshold, fixe onClick={handleViewAll} className="text-[10px] font-dmmono font-[500] text-[#000000] italic hover:text-[#FF5500] transition-colors cursor-pointer" > - VIEW ALL ▾ + VIEW ALL )} {/* Transactions */}
0 + className={`flex flex-col gap-3 ${allProposals.length > 0 ? fixedHeight - ? displayTransactions.length >= 5 - ? "h-[400px] overflow-hidden" // Fixed height for 5+ items - : "" // Dynamic height for less than 5 items - : "max-h-[240px] overflow-y-auto scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-gray-100" // Scrollable for 3 items (3 * 64px + 48px gap) - : "h-[200px]" // Default height when no items + ? allProposals.length >= 5 + ? "h-[400px] overflow-hidden" + : "" + : "max-h-[240px] overflow-y-auto scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-gray-100" + : "h-[200px]" }`} > - {transactionsLoading || initialLoad ? ( - // Loading spinner + {syncingState ? (

- Loading transactions... + Syncing...

- ) : displayTransactions.length > 0 ? ( - displayTransactions.map((tx: DecodedTransaction) => ( -
-
- {tx.created_at ? new Date(tx.created_at).toISOString().split('T')[0] : new Date().toISOString().split('T')[0]} -
-
-
- - {tx.transactionType === "sent" - ? `SEND Transaction - ${tx.amount.toFixed(2)} MIDEN` - : tx.transactionType === "received" - ? `RECEIVE Transaction - ${tx.amount.toFixed(2)} MIDEN` - : "Transaction"} - -
-
-
- {tx.transactionType -
-
-
- - {tx.signature_count}/{threshold} signed - -
-
- -
- - {tx.transactionType === "received" ? "+" : "-"} - {tx.amount.toFixed(2)} MIDEN - + ) : allProposals.length > 0 ? ( + allProposals.map((proposal) => { + const propThreshold = getEffectiveThreshold( + proposal.metadata?.proposalType, + effectiveThreshold, + detectedConfig?.procedureThresholds + ); + const sigCount = proposal.signatures?.length ?? 0; + const isSend = proposal.metadata?.proposalType === 'p2id'; + const isExecuted = proposal.status.type === 'finalized'; + + return ( +
+
+ {proposal.id.slice(0, 8)}... +
+
+
+ + {proposal.metadata?.proposalType === 'p2id' ? 'SEND Transaction' : + proposal.metadata?.proposalType === 'consume_notes' ? 'RECEIVE Transaction' : + proposal.metadata?.proposalType === 'add_signer' ? 'ADD SIGNER' : + proposal.metadata?.proposalType === 'remove_signer' ? 'REMOVE SIGNER' : + proposal.metadata?.proposalType === 'change_threshold' ? 'CHANGE THRESHOLD' : + proposal.metadata?.proposalType === 'switch_psm' ? 'SWITCH PSM' : + (proposal.metadata?.proposalType ?? 'UNKNOWN').toUpperCase()} + +
+
+
+ {isSend +
+
+
+ + {sigCount}/{propThreshold} signed + +
+
+
+ + {isExecuted ? "EXECUTED" : "PENDING"} + +
+
-
-
- )) + ); + }) ) : ( - // Empty state when no recent transactions
= () => { + const { + psmUrl, + setPsmUrl, + psmStatus, + connectToPsm, + activeCommitment, + activeScheme, + walletSource, + setWalletSource, + signer, + generatingSigner, + syncingState, + handleSync, + multisig, + paraSession, + midenWalletSession, + connectMidenWallet, + disconnectMidenWallet, + openParaModal, + } = useMultisig(); - const { walletData, loading, error } = useWalletData(); - const [isConnectWalletOpen, setIsConnectWalletOpen] = useState(false); - const [secretKeyHex, setSecretKeyHex] = useState(""); - const [approverAddress, setApproverAddress] = useState(""); - const [tabId, setTabId] = useState(""); - const [notification, setNotification] = useState<{ - type: "success" | "error"; - message: string; - } | null>(null); const [isCopied, setIsCopied] = useState(false); + const [showPsmEditor, setShowPsmEditor] = useState(false); + const [psmUrlDraft, setPsmUrlDraft] = useState(psmUrl); + const [showSignerKeys, setShowSignerKeys] = useState(false); - useEffect(() => { - const getTabId = () => { - let currentTabId = sessionStorage.getItem("tabId"); - if (!currentTabId) { - currentTabId = `tab_${Date.now()}_${Math.random() - .toString(36) - .substr(2, 9)}`; - sessionStorage.setItem("tabId", currentTabId); - } - setTabId(currentTabId); - return currentTabId; - }; - - const currentTabId = getTabId(); - loadWalletDataFromStorage(currentTabId); - }, []); - - const showNotification = (type: "success" | "error", message: string) => { - setNotification({ type, message }); - setTimeout(() => { - setNotification(null); - }, 4000); - }; - - const loadWalletDataFromStorage = (currentTabId: string) => { - const savedApproverAddress = localStorage.getItem( - `approver_address_${currentTabId}` - ); - const savedSecretKey = localStorage.getItem(`secret_key_${currentTabId}`); - - if (savedApproverAddress) { - setApproverAddress(savedApproverAddress); - + const walletName = useMemo(() => { + if (typeof window === 'undefined') return "Multisig Wallet"; + const saved = localStorage.getItem("walletFormData"); + if (saved) { + try { return JSON.parse(saved).walletName || "Multisig Wallet"; } catch { return "Multisig Wallet"; } } + return "Multisig Wallet"; + }, []); - if (savedSecretKey) { - setSecretKeyHex(savedSecretKey); - } - }; - - - const getConvertedWalletId = () => { - try { - const currentWalletId = localStorage.getItem('currentWalletId'); - if (!currentWalletId) return null; - - const walletAddress = Address.fromBech32(currentWalletId); - const walletAccountId = walletAddress.accountId(); - - const convertedWalletId = walletAccountId.toBech32(NetworkId.Testnet, AccountInterface.BasicWallet); - - - return convertedWalletId; - } catch (error) { - console.error('Error converting wallet ID:', error); - return localStorage.getItem('currentWalletId'); // Fallback to original - } - }; + const accountId = useMemo(() => { + return multisig?.accountId ?? localStorage.getItem("currentWalletId") ?? null; + }, [multisig]); - const copyWalletAddress = async () => { - try { - const convertedWalletId = getConvertedWalletId(); - if (convertedWalletId) { - await navigator.clipboard.writeText(convertedWalletId); + const copyAccountId = () => { + if (accountId) { + copyToClipboard(accountId, () => { setIsCopied(true); - setTimeout(() => setIsCopied(false), 2000); // Reset after 2 seconds - } - } catch (err) { - console.error('Failed to copy wallet address: ', err); + setTimeout(() => setIsCopied(false), 2000); + }); } }; - const handleConnectWallet = async () => { - - localStorage.setItem(`approver_address_${tabId}`, approverAddress); - localStorage.setItem(`secret_key_${tabId}`, secretKeyHex); - - showNotification("success", "Wallet connected successfully!"); - - setIsConnectWalletOpen(false); - }; - - const handleCloseConnectWalletModal = () => { - setIsConnectWalletOpen(false); + const handlePsmReconnect = async () => { + setPsmUrl(psmUrlDraft); + await connectToPsm(psmUrlDraft); + setShowPsmEditor(false); + toast.success("Reconnected to PSM"); }; - return ( -
+
- {/* Left side - Wallet info */} + {/* Left side - Account info */}
greyBox
- {loading ? "Loading..." : error ? "-" : walletData?.walletFormData?.walletName || "-"} + {walletName}
{ - const convertedWalletId = getConvertedWalletId(); - return convertedWalletId || "No Wallet ID"; - })()} + title={accountId || "No Account ID"} > - {loading ? "Loading..." : error ? "Error" : (() => { - const convertedWalletId = getConvertedWalletId(); - if (convertedWalletId && convertedWalletId.length > 10) { - return `${convertedWalletId.slice(0, 6)}..${convertedWalletId.slice(-4)}`; - } - return convertedWalletId || "No Wallet ID"; - })()} + {accountId ? truncateHex(accountId, 8, 6) : "No Account"}
- {/* Right side - Buttons */} - {/*
*/} - {/* Sync State Button */} - {/* */} + {/* Center - PSM Status & Wallet Source */} +
+ {/* PSM Status Badge */} +
+ - {/* Get Consumable Notes Button */} - {/* -
*/} + {/* PSM Endpoint Editor Popover */} + {showPsmEditor && ( +
+
PSM Endpoint
+ setPsmUrlDraft(e.target.value)} + className="w-full text-[11px] font-dmmono border border-gray-200 rounded px-2 py-1 mb-2 focus:outline-none focus:ring-1 focus:ring-[#FF5500]" + /> +
+ + +
+
+ )} +
-
- + {/* Wallet Source Selector */} +
+ + + +
-
- {/* Connect Wallet Modal */} - + {/* Right side - Signer Keys & Sync */} +
+ {/* Active Commitment Display */} +
+ - {/* Custom Notification */} - - {notification && ( - -
- - {notification.type === "success" ? "✅" : "❌"} - - {notification.message} -
-
- )} -
+ {/* Signer Keys Popover */} + {showSignerKeys && signer && ( +
+
Local Signer Keys
+
+
+
Falcon Commitment
+
copyToClipboard(signer.falcon.commitment, () => toast.success("Falcon commitment copied"))} + title="Click to copy" + > + {signer.falcon.commitment} +
+
+
+
ECDSA Commitment
+
copyToClipboard(signer.ecdsa.commitment, () => toast.success("ECDSA commitment copied"))} + title="Click to copy" + > + {signer.ecdsa.commitment} +
+
+
+ +
+ )} +
+ + {/* Sync Button */} + {multisig && ( + + )} +
+
); }; diff --git a/bin/coordinator-frontend/src/app/dashboard/home/page.tsx b/bin/coordinator-frontend/src/app/dashboard/home/page.tsx index a509261..12a7ad8 100644 --- a/bin/coordinator-frontend/src/app/dashboard/home/page.tsx +++ b/bin/coordinator-frontend/src/app/dashboard/home/page.tsx @@ -1,57 +1,42 @@ "use client"; -import React, { useEffect, useState } from "react"; +import React, { useMemo, useState } from "react"; import Image from "next/image"; import assetValIcon from "../../../../public/media/home/circum_dollar.svg"; import PendingActions from "../components/PendingActions"; import RecentTransactions from "../components/RecentTransactions"; -import { useAppDispatch } from "../../../store/hooks"; -import { useWalletData } from "../../../hooks/useWalletData"; - -import { - fetchPendingTransactions, - fetchConfirmedTransactions, -} from "../../../services/transactionApi"; +import { useMultisig } from "@/contexts/MultisigContext"; export const dynamic = 'force-dynamic'; import { AnimatePresence, motion } from "framer-motion"; import InitiateFundTransfer from "@/interactions/InitiateFundTransfer"; -import { useFungibleAssets } from "@/hooks/useFungibleAssets"; import ReceiveFundTransfer from "@/interactions/ReceiveFundTransfer"; import { ApproveFundTransfer } from "@/interactions/ApproveFundTransfer"; const Page: React.FC = () => { - const dispatch = useAppDispatch(); - const { walletData, loading, error } = useWalletData(); - const { fungibleAssets, isLoading: isLoadingAssets, getFungibleAssets } = useFungibleAssets(); - const [totalBalance, setTotalBalance] = useState(0); - const [isInitiateFundTransferOpen, setIsInitiateFundTransferOpen] = - useState(false); - const [isReceiveFundTransferOpen, setIsReceiveFundTransferOpen] = - useState(false); - const [isApproveFundTransferOpen, setIsApproveFundTransferOpen] = - useState(false); - - - useEffect(() => { - const walletId = localStorage.getItem("currentWalletId"); - if (walletId) { - dispatch(fetchPendingTransactions({ accountId: walletId })); - dispatch(fetchConfirmedTransactions({ accountId: walletId })); + const { detectedConfig, proposals, syncingState } = useMultisig(); + const [isInitiateFundTransferOpen, setIsInitiateFundTransferOpen] = useState(false); + const [isReceiveFundTransferOpen, setIsReceiveFundTransferOpen] = useState(false); + const [isApproveFundTransferOpen, setIsApproveFundTransferOpen] = useState(false); + + const walletName = useMemo(() => { + const saved = typeof window !== 'undefined' ? localStorage.getItem("walletFormData") : null; + if (saved) { + try { return JSON.parse(saved).walletName || "Multisig Wallet"; } catch { return "Multisig Wallet"; } } + return "Multisig Wallet"; }, []); - // Calculate total balance from fungible assets - useEffect(() => { - if (fungibleAssets.length > 0) { - const totalBalanceBigInt = fungibleAssets.reduce((sum, asset) => sum + BigInt(asset.balance), BigInt(0)); - const totalBalanceDisplay = Number(totalBalanceBigInt) / 1000000; - setTotalBalance(totalBalanceDisplay); - } else { - setTotalBalance(0); - } - }, [fungibleAssets]); + const threshold = detectedConfig?.threshold ?? 0; + const signerCount = detectedConfig?.signerCommitments?.length ?? 0; + const vaultBalances = detectedConfig?.vaultBalances ?? []; + + const totalBalance = useMemo(() => { + if (vaultBalances.length === 0) return 0; + const total = vaultBalances.reduce((sum, b) => sum + Number(b.amount), 0); + return total / 1000000; + }, [vaultBalances]); return (
@@ -69,7 +54,7 @@ const Page: React.FC = () => {
{totalBalance.toFixed(2)}
-
≈ ${totalBalance.toFixed(2)} USD
+
{vaultBalances.length} token(s) in vault
{/*Overview Div*/} @@ -85,29 +70,21 @@ const Page: React.FC = () => {
Wallet Name
- {loading ? "Loading..." : error ? "-" : walletData?.walletFormData?.walletName || "Multisig Wallet"} + {walletName}
-
Kind
+
Signers
- {loading ? "Loading..." : error ? "-" : walletData?.kind || "N/A"} + {signerCount}
Threshold
- {loading - ? "Loading..." - : error - ? "-" - : walletData - ? `${walletData.threshold} of ${walletData.approver_number} signatures` - : "N/A"} + {threshold > 0 ? `${threshold} of ${signerCount} signatures` : "N/A"}
- -
@@ -152,13 +129,13 @@ const Page: React.FC = () => { {/*Pending Actions Div*/}
{/*Recent Transactions Div*/}
- +
{/* initiate fund transfer interactions is being called here */} @@ -180,9 +157,6 @@ const Page: React.FC = () => { > setIsInitiateFundTransferOpen(false)} - fungibleAssets={fungibleAssets} - isLoading={isLoadingAssets} - onOpen={getFungibleAssets} /> @@ -207,7 +181,6 @@ const Page: React.FC = () => { > setIsReceiveFundTransferOpen(false)} - onAssetsUpdated={getFungibleAssets} /> @@ -237,10 +210,6 @@ const Page: React.FC = () => { )} - - - -
); }; diff --git a/bin/coordinator-frontend/src/app/dashboard/settings/components/General.tsx b/bin/coordinator-frontend/src/app/dashboard/settings/components/General.tsx index 4be61c7..6b6aab9 100644 --- a/bin/coordinator-frontend/src/app/dashboard/settings/components/General.tsx +++ b/bin/coordinator-frontend/src/app/dashboard/settings/components/General.tsx @@ -1,37 +1,28 @@ -import React, { useState } from "react"; +import React, { useState, useMemo } from "react"; import media from "../../../../../public/media"; import Image from "next/image"; -import { useWalletData } from "@/hooks/useWalletData"; -import { Address, NetworkId, AccountInterface } from "@demox-labs/miden-sdk"; +import { useMultisig } from "@/contexts/MultisigContext"; +import { copyToClipboard, truncateHex } from "@/lib/helpers"; +import { toast } from "sonner"; const General = () => { - const { walletData, loading, error } = useWalletData(); + const { detectedConfig, multisig, syncingState } = useMultisig(); const signerIcons = [media.signer1, media.signer2, media.signer3]; const [showTooltip, setShowTooltip] = useState(false); - const getConvertedWalletId = () => { - try { - const currentWalletId = localStorage.getItem("currentWalletId"); - if (!currentWalletId) return null; - const walletAddress = Address.fromBech32(currentWalletId); - const walletAccountId = walletAddress.accountId(); - const convertedWalletId = walletAccountId.toBech32( - NetworkId.Testnet, - AccountInterface.BasicWallet - ); - return convertedWalletId; - } catch (error) { - console.error("Error converting wallet ID:", error); - return localStorage.getItem("currentWalletId"); - } - }; + const signerCommitments = detectedConfig?.signerCommitments ?? []; + const accountId = multisig?.accountId ?? localStorage.getItem("currentWalletId") ?? null; - const copyToClipboard = async (text: string) => { - try { - await navigator.clipboard.writeText(text); - } catch (err) { - console.error("Failed to copy: ", err); + const walletName = useMemo(() => { + const saved = typeof window !== 'undefined' ? localStorage.getItem("walletFormData") : null; + if (saved) { + try { return JSON.parse(saved).walletName || "Multisig Wallet"; } catch { return "Multisig Wallet"; } } + return "Multisig Wallet"; + }, []); + + const handleCopy = (text: string) => { + copyToClipboard(text, () => toast.success("Copied!")); }; return ( @@ -42,8 +33,7 @@ const General = () => { ACCOUNT SIGNERS
- {loading ? ( - // Loading spinner + {syncingState ? (
@@ -52,15 +42,8 @@ const General = () => {

- ) : error ? ( -
-

- Error loading signers: {error} -

-
- ) : walletData?.approver && - walletData.approver.length > 0 ? ( - walletData.approver.map((address, index) => ( + ) : signerCommitments.length > 0 ? ( + signerCommitments.map((commitment, index) => (
{
- {address} + {truncateHex(commitment, 20, 10)}
{/* wallet information and network settings */} -
- {" "} Wallet information
@@ -129,13 +110,13 @@ const General = () => { Wallet Name - {walletData?.walletFormData?.walletName || "Unknown Wallet"} + {walletName}
- Address + Account ID
@@ -144,23 +125,14 @@ const General = () => { onMouseEnter={() => setShowTooltip(true)} onMouseLeave={() => setShowTooltip(false)} > - {(() => { - const convertedWalletId = getConvertedWalletId(); - if (convertedWalletId && convertedWalletId.length > 10) { - return `${convertedWalletId.slice( - 0, - 6 - )}..${convertedWalletId.slice(-4)}`; - } - return convertedWalletId || "No Wallet ID"; - })()} + {accountId ? truncateHex(accountId, 8, 6) : "No Account ID"} - {getConvertedWalletId() && ( + {accountId && (
- {/* Custom tooltip */} - {showTooltip && getConvertedWalletId() && ( + {showTooltip && accountId && (
- {getConvertedWalletId()} + {accountId}
)} @@ -195,7 +166,6 @@ const General = () => {
- {" "} NETWORK SETTINGS
@@ -204,7 +174,7 @@ const General = () => { Current Network - {walletData?.walletFormData?.network || "Unknown Network"} + Miden Devnet
diff --git a/bin/coordinator-frontend/src/app/dashboard/settings/components/Security.tsx b/bin/coordinator-frontend/src/app/dashboard/settings/components/Security.tsx index 77670dd..65aadee 100644 --- a/bin/coordinator-frontend/src/app/dashboard/settings/components/Security.tsx +++ b/bin/coordinator-frontend/src/app/dashboard/settings/components/Security.tsx @@ -1,25 +1,131 @@ import React from "react"; -import DynamicWalletButton from "../../../../components/DynamicWalletButton"; -import { WalletInfo } from "../../../../components/WalletInfo"; -import { useWallet } from "@demox-labs/miden-wallet-adapter"; +import { useMultisig } from "@/contexts/MultisigContext"; +import { copyToClipboard, truncateHex } from "@/lib/helpers"; +import { toast } from "sonner"; const Security = () => { - const { connected } = useWallet(); + const { + walletSource, + setWalletSource, + activeCommitment, + activeScheme, + signer, + paraSession, + midenWalletSession, + connectMidenWallet, + disconnectMidenWallet, + openParaModal, + psmUrl, + psmStatus, + } = useMultisig(); + + const handleCopy = (text: string) => { + copyToClipboard(text, () => toast.success("Copied!")); + }; return ( -
+
- {/* Wallet Connection Section */} + {/* Wallet Source Section */}
- +

WALLET SOURCE

+
+ + + +
+ + {/* Active Commitment */} +
+
Active Commitment ({activeScheme})
+
activeCommitment && handleCopy(activeCommitment)} + title="Click to copy" + > + {activeCommitment || "N/A"} +
+
+ + {/* Disconnect Miden Wallet */} + {midenWalletSession.connected && walletSource === 'miden-wallet' && ( + + )}
- {/* Wallet Info Section - Only show when connected */} - {connected && ( + {/* Local Signer Keys Section */} + {signer && (
- +

LOCAL SIGNER KEYS

+
+
+
Falcon Commitment
+
handleCopy(signer.falcon.commitment)} + title="Click to copy" + > + {signer.falcon.commitment} +
+
+
+
ECDSA Commitment
+
handleCopy(signer.ecdsa.commitment)} + title="Click to copy" + > + {signer.ecdsa.commitment} +
+
+
)} + + {/* PSM Connection */} +
+

PSM CONNECTION

+
+ + {psmStatus} +
+
{psmUrl}
+
); diff --git a/bin/coordinator-frontend/src/app/dashboard/transactions/page.tsx b/bin/coordinator-frontend/src/app/dashboard/transactions/page.tsx index b890449..9a1ec15 100644 --- a/bin/coordinator-frontend/src/app/dashboard/transactions/page.tsx +++ b/bin/coordinator-frontend/src/app/dashboard/transactions/page.tsx @@ -1,36 +1,26 @@ "use client"; -import React, { useEffect } from "react"; +import React, { useMemo } from "react"; import Image from "next/image"; import media from "../../../../public/media"; import PendingActions from "../components/PendingActions"; import RecentTransactions from "../components/RecentTransactions"; -import { useAppDispatch, useAppSelector } from "../../../store/hooks"; -import { useWalletData } from "../../../hooks/useWalletData"; -import { useFungibleAssets } from "../../../hooks/useFungibleAssets"; - -import { - fetchPendingTransactions, - fetchConfirmedTransactions, - fetchTransactionStatsThunk, -} from "../../../services/transactionApi"; +import { useMultisig } from "@/contexts/MultisigContext"; // Force dynamic rendering to avoid WASM loading issues during build export const dynamic = 'force-dynamic'; const Transactions: React.FC = () => { - const dispatch = useAppDispatch(); - const { fungibleAssets } = useFungibleAssets(); - const { walletData } = useWalletData(); - const { stats, loading: statsLoading } = useAppSelector((state) => state.walletStats); + const { proposals, detectedConfig } = useMultisig(); + + const threshold = detectedConfig?.threshold ?? 0; - useEffect(() => { - const walletId = localStorage.getItem("currentWalletId"); - if (walletId) { - dispatch(fetchPendingTransactions({ accountId: walletId })); - dispatch(fetchConfirmedTransactions({ accountId: walletId })); - dispatch(fetchTransactionStatsThunk({ accountId: walletId })); - } - }, [dispatch]); + const stats = useMemo(() => { + const total = proposals.length; + const executed = proposals.filter(p => p.status.type === 'finalized').length; + const pending = proposals.filter(p => p.status.type === 'pending' || p.status.type === 'ready').length; + const successRate = total > 0 ? Math.round((executed / total) * 100) : 0; + return { total, executed, pending, successRate }; + }, [proposals]); return (
@@ -45,7 +35,7 @@ const Transactions: React.FC = () => {
{/*Top Cards Div*/}
- {/*Total Asset Value Div*/} + {/*Total Transactions Div*/}
{ quality={100} />
- Total Transactions + Total Proposals
- {statsLoading ? "..." : stats?.total || 0} + {stats.total}
All Time
- {/*This Month Div*/} + {/*Pending Div*/}
{ quality={100} />
- This Month + Pending
- {statsLoading ? "..." : stats?.last_month || 0} + {stats.pending}
-
Last 30 days
+
Awaiting signatures
{/*Success Rate Div*/} @@ -88,25 +78,25 @@ const Transactions: React.FC = () => {
assetValIcon
- Success Rate + Execution Rate
- {statsLoading ? "..." : stats?.total ? Math.round((stats.total_success / stats.total) * 100) : 0}% + {stats.successRate}%
-
{stats?.total_success || 0}/{stats?.total || 0} Success
+
{stats.executed}/{stats.total} Executed
{/*Pending Actions Div*/}
- +
{/*Recent Transactions Div*/}
- +
); diff --git a/bin/coordinator-frontend/src/app/login/createNewAccount/page.tsx b/bin/coordinator-frontend/src/app/login/createNewAccount/page.tsx index e8ca72e..2d33a6d 100644 --- a/bin/coordinator-frontend/src/app/login/createNewAccount/page.tsx +++ b/bin/coordinator-frontend/src/app/login/createNewAccount/page.tsx @@ -5,8 +5,10 @@ import { useRouter } from "next/navigation"; import { motion, AnimatePresence } from "framer-motion"; import Svg from "../../../../public/svg"; import { useWalletForm } from "../../../hooks/useWalletForm"; -import { createMultiSigWallet } from "../../../services/walletApi"; import { useAuth } from "../../../hooks/useAuth"; +import { useMultisig } from "@/contexts/MultisigContext"; +import { truncateHex } from "@/lib/helpers"; +import { toast } from "sonner"; // Force dynamic rendering to avoid WASM loading issues during build export const dynamic = 'force-dynamic'; @@ -14,6 +16,7 @@ export const dynamic = 'force-dynamic'; const CreateNewAccount = () => { const router = useRouter(); const { setWalletId } = useAuth(); + const { handleCreate, creating, activeScheme, activeCommitment, walletSource, error: multisigError } = useMultisig(); const { formData, currentStep, @@ -36,6 +39,27 @@ const CreateNewAccount = () => { signatureThreshold: false, totalSigners: false, }); + // Compute the wallet source label for Signer 1 + const walletSourceLabel = walletSource === 'para' + ? 'You (Para)' + : walletSource === 'miden-wallet' + ? 'You (Miden Wallet)' + : 'You (Local)'; + + // Auto-populate Signer 1 with the active wallet commitment. + // Only dispatch when the values actually change to avoid render loops + // (updateSigner/updateSignerPublicKeyField are not memoized). + const prevCommitmentRef = useRef(null); + const prevLabelRef = useRef(null); + useEffect(() => { + if (activeCommitment && (activeCommitment !== prevCommitmentRef.current || walletSourceLabel !== prevLabelRef.current)) { + prevCommitmentRef.current = activeCommitment; + prevLabelRef.current = walletSourceLabel; + updateSigner(0, walletSourceLabel); + updateSignerPublicKeyField(0, activeCommitment); + } + }); // intentionally no deps — runs every render but guards with ref check + const hasRestoredRef = useRef(false); useEffect(() => { if (hasRestoredRef.current) return; @@ -47,11 +71,13 @@ const CreateNewAccount = () => { Object.entries(parsedData).forEach(([key, value]) => { if (key === "signerAddresses") { (value as string[]).forEach((address, index) => { - updateSigner(index, address); + // Skip signer 0 - it's auto-populated from wallet + if (index > 0) updateSigner(index, address); }); } else if (key === "signerPublicKeys") { (value as string[]).forEach((publicKey, index) => { - updateSignerPublicKeyField(index, publicKey); + // Skip signer 0 - it's auto-populated from wallet + if (index > 0) updateSignerPublicKeyField(index, publicKey); }); } else { updateField(key, value as string); @@ -105,6 +131,8 @@ const CreateNewAccount = () => { const handleSignerPublicKeyChange = (index: number, value: string) => { updateSignerPublicKeyField(index, value); + // Auto-sync address from commitment (they're the same value) + updateSigner(index, value); }; const handleAddSignerAddress = () => { @@ -146,8 +174,16 @@ const CreateNewAccount = () => { setCreationError(null); try { - const result = await createMultiSigWallet(formData); - setWalletId(result.address); + // Signer 1 (index 0) is "us" — handled internally by handleCreate. + // Only pass the "other" signer commitments (index 1+). + const otherCommitments = formData.signerPublicKeys + .slice(1) + .filter((k: string) => k.trim() !== ''); + const threshold = parseInt(formData.signatureThreshold, 10); + + await handleCreate(otherCommitments, threshold, undefined, activeScheme); + + toast.success("Multisig account created successfully!"); router.push("/dashboard/home"); } catch (error) { console.error("Failed to create wallet:", error); @@ -363,7 +399,7 @@ const CreateNewAccount = () => { ADD SIGNERS
- Add account addresses that will be authorized to sign + Add signer commitments that will be authorized to sign transactions
@@ -380,68 +416,64 @@ const CreateNewAccount = () => { transition={{ duration: 0.25, ease: "easeInOut" }} className="w-full flex flex-col lg:space-y-1 md:space-y-1.5 sm:space-y-1 space-y-1" > -
- Signer {activeSignerIndex + 1} Address -
-
- - handleSignerAddressChange( - activeSignerIndex, - e.target.value - ) - } - className="bg-[rgba(245,245,245,1)] w-full lg:h-[44px] md:h-[40px] sm:h-[36px] h-[32px] border-[1.09px] border-[rgba(217,217,217,1)] rounded-md px-3 font-dmmono font-[500] text-[12px] focus:outline-none focus:ring-2 focus:ring-[#FF5500]/60" - /> - {formData.signerAddresses.length > 1 && ( - - )} -
- -
- Signer {activeSignerIndex + 1} Public Key -
-
- - handleSignerPublicKeyChange( - activeSignerIndex, - e.target.value - ) - } - placeholder="0x..." - className="bg-[rgba(245,245,245,1)] w-full lg:h-[44px] md:h-[40px] sm:h-[36px] h-[32px] border-[1.09px] border-[rgba(217,217,217,1)] rounded-md px-3 font-dmmono font-[500] text-[12px] focus:outline-none focus:ring-2 focus:ring-[#FF5500]/60" - /> -
+ {activeSignerIndex === 0 ? ( + <> +
+ Signer 1 — {walletSourceLabel} +
+
+ {activeCommitment || 'Generating keys...'} +
+ + ) : ( + <> +
+ Signer {activeSignerIndex + 1} Commitment +
+
+ + handleSignerPublicKeyChange( + activeSignerIndex, + e.target.value + ) + } + placeholder="0x..." + className="bg-[rgba(245,245,245,1)] w-full lg:h-[44px] md:h-[40px] sm:h-[36px] h-[32px] border-[1.09px] border-[rgba(217,217,217,1)] rounded-md px-3 font-dmmono font-[500] text-[12px] focus:outline-none focus:ring-2 focus:ring-[#FF5500]/60" + /> + {formData.signerAddresses.length > 1 && ( + + )} +
+ + )}
@@ -457,11 +489,11 @@ const CreateNewAccount = () => { ? "bg-[#FF5500] border-[#FF5500] text-white" : "bg-white border-[rgba(217,217,217,1)] text-[rgba(0,0,0,0.6)] hover:border-[#FF5500] hover:text-[#FF5500]" }`} - aria-label={`Go to signer ${i + 1}`} - title={`Go to signer ${i + 1}`} + aria-label={i === 0 ? "Go to signer 1 (You)" : `Go to signer ${i + 1}`} + title={i === 0 ? "Signer 1 (You)" : `Signer ${i + 1}`} > - {i + 1} + {i === 0 ? "\u2605" : i + 1} ))} @@ -480,9 +512,9 @@ const CreateNewAccount = () => {
- Security Note: Each signer should verify their address and - public key are correct. Incorrect addresses or public keys - cannot be easily changed after deployment. + Security Note: Each signer should verify their commitment is + correct. Incorrect commitments cannot be easily changed + after deployment.
@@ -557,19 +589,24 @@ const CreateNewAccount = () => { className="w-full max-h-[110px] overflow-y-auto space-y-2 scrollbar-thin pr-1 select-none" > {formData.signerAddresses.map( - (address: string, index: number) => ( + (_: string, index: number) => (
- Signer {index + 1}: {address || "Not specified"} + {index === 0 ? ( + Signer 1: {walletSourceLabel} + ) : ( + Signer {index + 1} + )}
-
- Public Key:{" "} +
{formData.signerPublicKeys[index] || "Not specified"}
diff --git a/bin/coordinator-frontend/src/app/login/loadExistingAccount/page.tsx b/bin/coordinator-frontend/src/app/login/loadExistingAccount/page.tsx index 7590f83..b9f5baf 100644 --- a/bin/coordinator-frontend/src/app/login/loadExistingAccount/page.tsx +++ b/bin/coordinator-frontend/src/app/login/loadExistingAccount/page.tsx @@ -2,7 +2,8 @@ import React, { useState } from "react"; import { useRouter } from "next/navigation"; import { useAuth } from "../../../hooks/useAuth"; -import { Address, NetworkId } from "@demox-labs/miden-sdk"; +import { useMultisig } from "@/contexts/MultisigContext"; +import { toast } from "sonner"; // Force dynamic rendering to avoid WASM loading issues during build export const dynamic = 'force-dynamic'; @@ -10,47 +11,51 @@ export const dynamic = 'force-dynamic'; const LoadExistingWallet = () => { const router = useRouter(); const { setWalletId } = useAuth(); - const [accountAddress, setWalletAddress] = useState(""); - const [accountName, setWalletName] = useState(""); + const { handleLoad, loadingAccount, error: multisigError, activeScheme } = useMultisig(); + const [accountId, setAccountId] = useState(""); + const [accountName, setAccountName] = useState(""); + const [localError, setLocalError] = useState(null); - const handleLoadWallet = () => { - if (accountAddress.trim() && accountName.trim()) { - try { - // Convert user entered address to Address object - const address = Address.fromBech32(accountAddress.trim()); - - // Extract account_id - const account_id = address.accountId(); - - // Create new address with "BasicWallet" kind - const address_new = Address.fromAccountId(account_id, "BasicWallet"); + const handleLoadWallet = async () => { + if (!accountId.trim() || !accountName.trim()) { + setLocalError("Please enter both account name and account ID."); + return; + } - // Get the correct bech32 address - const bech32_new = address_new.toBech32(NetworkId.Testnet); + setLocalError(null); + try { + await handleLoad(accountId.trim(), activeScheme); - // Store walletFormData with just walletName - const walletFormData = { - walletName: accountName.trim(), - signatureThreshold: "", - totalSigners: "", - network: "", - signerAddresses: [], - signerPublicKeys: [] - }; - localStorage.setItem("walletFormData", JSON.stringify(walletFormData)); + // Store walletFormData with just walletName + const walletFormData = { + walletName: accountName.trim(), + signatureThreshold: "", + totalSigners: "", + network: "", + signerAddresses: [], + signerPublicKeys: [] + }; + localStorage.setItem("walletFormData", JSON.stringify(walletFormData)); - // Set the converted address in localStorage and navigate - setWalletId(bech32_new); - localStorage.setItem("currentWalletId", bech32_new); - router.push("/dashboard/home"); - } catch (error) { - console.error("Error converting account address:", error); - alert("Invalid account address. Please enter a valid address."); + // Normalize account ID for storage + let normalizedId = accountId.trim(); + if (!normalizedId.startsWith('0x')) { + normalizedId = `0x${normalizedId}`; } - } else { - alert("Please enter both wallet name and address."); + + setWalletId(normalizedId); + localStorage.setItem("currentWalletId", normalizedId); + router.push("/dashboard/home"); + } catch (error) { + console.error("Error loading account:", error); + const msg = error instanceof Error ? error.message : "Failed to load account"; + setLocalError(msg); + toast.error(msg); } }; + + const displayError = localError || multisigError; + return ( <>
@@ -68,10 +73,10 @@ const LoadExistingWallet = () => {
- ENTER ACCOUNT ADDRESS + ENTER ACCOUNT ID
- Paste the address of the account you want to load + Paste the hex account ID of the multisig account you want to load
@@ -80,21 +85,28 @@ const LoadExistingWallet = () => { setWalletName(e.target.value)} + onChange={(e) => setAccountName(e.target.value)} placeholder="Enter account name" className="bg-[rgba(245,245,245,1)] w-full lg:h-[44px] md:h-[40px] sm:h-[36px] h-[32px] border-[1.09px] border-[rgba(217,217,217,1)] px-3 font-dmmono font-[500] text-[12px]" />
- ACCOUNT ADDRESS + ACCOUNT ID
setWalletAddress(e.target.value)} - placeholder="Enter account address" + value={accountId} + onChange={(e) => setAccountId(e.target.value)} + placeholder="0x..." className="bg-[rgba(245,245,245,1)] w-full lg:h-[44px] md:h-[40px] sm:h-[36px] h-[32px] border-[1.09px] border-[rgba(217,217,217,1)] px-3 font-dmmono font-[500] text-[12px]" /> + + {/* Error Display */} + {displayError && ( +
+ {displayError} +
+ )}
@@ -108,9 +120,10 @@ const LoadExistingWallet = () => {
{/* button section ends here */} @@ -121,4 +134,3 @@ const LoadExistingWallet = () => { }; export default LoadExistingWallet; - diff --git a/bin/coordinator-frontend/src/components/AppHeader.tsx b/bin/coordinator-frontend/src/components/AppHeader.tsx new file mode 100644 index 0000000..76aa3f3 --- /dev/null +++ b/bin/coordinator-frontend/src/components/AppHeader.tsx @@ -0,0 +1,271 @@ +'use client'; + +import React, { useState, useEffect, useRef } from 'react'; +import { useMultisig } from '@/contexts/MultisigContext'; +import { copyToClipboard, truncateHex } from '@/lib/helpers'; +import { toast } from 'sonner'; + +export function AppHeader() { + const { + signer, + generatingSigner, + activeScheme, + multisig, + walletSource, + setWalletSource, + paraSession, + midenWalletSession, + connectMidenWallet, + disconnectMidenWallet, + openParaModal, + psmStatus, + psmUrl, + setPsmUrl, + connectToPsm, + } = useMultisig(); + + const [psmPopoverOpen, setPsmPopoverOpen] = useState(false); + const [walletPopoverOpen, setWalletPopoverOpen] = useState(false); + const [keysPopoverOpen, setKeysPopoverOpen] = useState(false); + const [urlInput, setUrlInput] = useState(psmUrl); + + const psmRef = useRef(null); + const walletRef = useRef(null); + const keysRef = useRef(null); + + useEffect(() => { setUrlInput(psmUrl); }, [psmUrl]); + + // Close popovers on outside click + useEffect(() => { + const handler = (e: MouseEvent) => { + if (psmRef.current && !psmRef.current.contains(e.target as Node)) setPsmPopoverOpen(false); + if (walletRef.current && !walletRef.current.contains(e.target as Node)) setWalletPopoverOpen(false); + if (keysRef.current && !keysRef.current.contains(e.target as Node)) setKeysPopoverOpen(false); + }; + document.addEventListener('mousedown', handler); + return () => document.removeEventListener('mousedown', handler); + }, []); + + const handlePsmSave = () => { + setPsmUrl(urlInput); + connectToPsm(urlInput); + setPsmPopoverOpen(false); + }; + + const handleCopy = (text: string, label: string) => { + copyToClipboard(text, () => toast.success(`${label} copied`)); + }; + + return ( +
+
Miden Multisig
+ +
+ {/* Wallet Source Selector */} +
+ + {walletPopoverOpen && ( +
+
WALLET SOURCE
+
+ + + {paraSession.connected ? ( + + ) : ( + + )} + {paraSession.connected && paraSession.commitment && ( +
handleCopy(paraSession.commitment!, 'Para commitment')} + > + {truncateHex(paraSession.commitment, 10, 6)} +
+ )} + + {midenWalletSession.connected ? ( +
+ + +
+ ) : ( + + )} + {midenWalletSession.connected && midenWalletSession.commitment && ( +
handleCopy(midenWalletSession.commitment!, 'Wallet commitment')} + > + {truncateHex(midenWalletSession.commitment, 10, 6)} +
+ )} +
+
+ )} +
+ + {/* Signer Keys */} + {generatingSigner ? ( + Generating keys... + ) : signer ? ( +
+ + {keysPopoverOpen && ( +
+
LOCAL SIGNER KEYS
+
+ {multisig?.accountId && ( +
+
Account Address
+
handleCopy(multisig.accountId, 'Account address')} + title="Click to copy" + > + {truncateHex(multisig.accountId, 12, 8)} +
+
+ )} +
+
+ + FALCON + + {activeScheme === 'falcon' && walletSource === 'local' && ( + active + )} +
+
handleCopy(signer.falcon.commitment, 'Falcon commitment')} + title="Click to copy" + > + {truncateHex(signer.falcon.commitment, 12, 8)} +
+
+
+
+ + ECDSA + + {activeScheme === 'ecdsa' && walletSource === 'local' && ( + active + )} +
+
handleCopy(signer.ecdsa.commitment, 'ECDSA commitment')} + title="Click to copy" + > + {truncateHex(signer.ecdsa.commitment, 12, 8)} +
+
+
Click to copy
+
+
+ )} +
+ ) : null} + + {/* PSM Status */} +
+ + {psmPopoverOpen && ( +
+
PSM CONFIGURATION
+
+ {psmStatus === 'connected' ? 'Connected to PSM server' : + psmStatus === 'connecting' ? 'Connecting...' : 'Failed to connect'} +
+
+ + setUrlInput(e.target.value)} + placeholder="https://psm-stg.openzeppelin.com" + className="w-full px-2 py-1.5 border border-[#00000019] rounded text-[11px] focus:outline-none focus:border-[#FF5500]" + /> +
+ +
+ )} +
+
+
+ ); +} diff --git a/bin/coordinator-frontend/src/components/ConnectWalletModal.tsx b/bin/coordinator-frontend/src/components/ConnectWalletModal.tsx deleted file mode 100644 index d114f2a..0000000 --- a/bin/coordinator-frontend/src/components/ConnectWalletModal.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React from "react"; -import { ConnectWalletModalProps } from "@/types"; - -const ConnectWalletModal: React.FC = ({ - isOpen, - onClose, - secretKey, - setSecretKey, - approverAddress, - setApproverAddress, - onConnect, -}) => { - if (!isOpen) return null; - - return ( -
-
-
-

Connect Wallet

- -
- -
- - setSecretKey(e.target.value)} - placeholder="Enter your secret key (hex format)" - className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#FF5500] focus:border-transparent font-dmmono text-sm" - /> -
- -
- - setApproverAddress(e.target.value)} - placeholder="Enter approver address (e.g., mtst1qp27lvqjergeyypathdms4069ccv55l7)" - className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#FF5500] focus:border-transparent font-dmmono text-sm" - /> -
- -
- - -
-
-
- ); -}; - -export default ConnectWalletModal; diff --git a/bin/coordinator-frontend/src/components/DynamicWalletButton.tsx b/bin/coordinator-frontend/src/components/DynamicWalletButton.tsx deleted file mode 100644 index 7ed6820..0000000 --- a/bin/coordinator-frontend/src/components/DynamicWalletButton.tsx +++ /dev/null @@ -1,48 +0,0 @@ -'use client'; - -import React, { useState, useEffect } from 'react'; -import { WalletAdapterNetwork } from '@demox-labs/miden-wallet-adapter-base'; - -const DynamicWalletButton: React.FC = () => { - const [WalletMultiButton, setWalletMultiButton] = useState | null>(null); - const [isLoading, setIsLoading] = useState(true); - - useEffect(() => { - const loadWalletAdapter = async () => { - try { - const { WalletMultiButton } = await import('@demox-labs/miden-wallet-adapter'); - setWalletMultiButton(() => WalletMultiButton); - } catch (error) { - console.error('Failed to load wallet adapter:', error); - } finally { - setIsLoading(false); - } - }; - - loadWalletAdapter(); - }, []); - - if (isLoading) { - return ( -
-
-
-
-
- ); - } - - if (!WalletMultiButton) { - return ( -
-
Failed to load wallet adapter
-
- ); - } - - return ( - - ); -}; - -export default DynamicWalletButton; diff --git a/bin/coordinator-frontend/src/components/Providers.tsx b/bin/coordinator-frontend/src/components/Providers.tsx index 180abe1..e98d4a4 100644 --- a/bin/coordinator-frontend/src/components/Providers.tsx +++ b/bin/coordinator-frontend/src/components/Providers.tsx @@ -1,64 +1,66 @@ 'use client'; +import React, { useState, useEffect } from 'react'; import { Provider } from 'react-redux'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { ParaProvider, Environment } from '@getpara/react-sdk-lite'; +import { Toaster } from 'sonner'; + import { store } from '../store'; -import { useState, useEffect } from 'react'; -import { - WalletProvider, - WalletModalProvider, - MidenWalletAdapter, - PrivateDataPermission, -} from '@demox-labs/miden-wallet-adapter'; -import { WalletAdapterNetwork } from '@demox-labs/miden-wallet-adapter-base'; -import { MidenSdkProvider } from '../hooks/useMidenSdk'; -import { MidenClientProvider } from '../contexts/MidenClientContext'; +import { MultisigProvider, useMultisig } from '../contexts/MultisigContext'; +import { AppHeader } from './AppHeader'; +import { PARA_API_KEY, PARA_ENVIRONMENT } from '@/config/psm'; + +import '@getpara/react-sdk-lite/styles.css'; + +const queryClient = new QueryClient(); +const paraEnv = PARA_ENVIRONMENT === 'production' ? Environment.PROD : Environment.DEV; -// Import styles -import '@demox-labs/miden-wallet-adapter/styles.css'; +function ParaModalWrapper() { + const { paraModalOpen, closeParaModal } = useMultisig(); + + const [ParaModal, setParaModal] = useState void }> | null>(null); + + useEffect(() => { + if (paraModalOpen && !ParaModal) { + import('@getpara/react-sdk-lite').then((mod) => { + setParaModal(() => mod.ParaModal); + }).catch(() => {}); + } + }, [paraModalOpen, ParaModal]); + + if (!ParaModal) return null; + return ; +} export function Providers({ children }: { children: React.ReactNode }) { - const [wallets, setWallets] = useState([]); const [mounted, setMounted] = useState(false); useEffect(() => { setMounted(true); - - const midenAdapter = new MidenWalletAdapter({ - appName: 'Miden Wallet App', - }); - - setWallets([midenAdapter]); }, []); - // Render basic providers during SSR, full wallet providers only on client if (!mounted) { - return ( - - - - {children} - - - - ); + return null; } + // Always wrap with ParaProvider so useParaMiden hook has its context. + // When no API key is set, Para features simply won't work but the provider won't crash. return ( - - - - - - {children} - - - - - + + + + + + {children} + {PARA_API_KEY && } + + + + + ); -} \ No newline at end of file +} diff --git a/bin/coordinator-frontend/src/components/TransferBox.tsx b/bin/coordinator-frontend/src/components/TransferBox.tsx deleted file mode 100644 index a35b30f..0000000 --- a/bin/coordinator-frontend/src/components/TransferBox.tsx +++ /dev/null @@ -1,109 +0,0 @@ -"use client"; -import React from "react"; -import { Transaction, TransferBoxProps } from "../types"; -import media from "../../public/media"; -import Image from "next/image"; - -export const TransferBox: React.FC = ({ - transaction, - index, - threshold = 3, - onApprove, - isSelected = false, - onSelect, -}) => { - // Function to convert hex string to JSON object (same as PendingActions) - const hexToJson = (hexString: string) => { - try { - // Remove 0x prefix if present - const cleanHex = hexString.startsWith('0x') ? hexString.slice(2) : hexString; - const jsonString = Buffer.from(cleanHex, 'hex').toString('utf8'); - return JSON.parse(jsonString); - } catch (error) { - console.error('Error converting hex to JSON:', error); - return null; - } - }; - - // Function to calculate time ago from timestamp (same as PendingActions) - const getTimeAgo = (timestamp: number) => { - const now = Date.now(); - const diffInSeconds = Math.floor((now - timestamp) / 1000); - - if (diffInSeconds < 60) { - return `${diffInSeconds}s ago`; - } else if (diffInSeconds < 3600) { - const minutes = Math.floor(diffInSeconds / 60); - return `${minutes}m ago`; - } else if (diffInSeconds < 86400) { - const hours = Math.floor(diffInSeconds / 3600); - return `${hours}h ago`; - } else { - const days = Math.floor(diffInSeconds / 86400); - return `${days}d ago`; - } - }; - - // Decode transaction data using the same method as PendingActions - const decodedData = transaction.tx_summary_commit ? hexToJson(transaction.tx_summary_commit) : null; - - // Extract data with fallbacks (same structure as PendingActions) - const amount = decodedData?.amount || '0.00'; - const recipient = decodedData?.recipient || 'Unknown'; - const currency = decodedData?.currency || 'MIDEN'; - const type = decodedData?.type || 'TRANSFER'; - const memo = decodedData?.memo || 'No memo'; - const timeAgo = decodedData?.timestamp ? getTimeAgo(decodedData.timestamp) : 'Unknown'; - - // Truncate recipient address for display - const truncatedRecipient = recipient.length > 20 - ? `${recipient.substring(0, 10)}...${recipient.substring(recipient.length - 10)}` - : recipient; - - return ( -
-
- {/* left side - checkbox and content */} -
- {/* Selection indicator - orange box when selected */} - {onSelect && ( -
onSelect(transaction.id)} - className={`w-4 h-4 cursor-pointer transition-all duration-200 ${ - isSelected - ? 'bg-[#FF5500] scale-110' - : 'bg-gray-200 hover:bg-gray-300' - }`} - /> - )} - - {/* Transaction details */} -
- - {type} {amount} {currency} to {truncatedRecipient} - -
-
- {transaction.signature_count}/{threshold} signed - - {timeAgo} -
-
-
-
- - {/*right side - status badges */} -
-
- STANDARD -
-
- {threshold - transaction.signature_count} needed -
-
-
-
- ); -}; diff --git a/bin/coordinator-frontend/src/components/WalletConnection.tsx b/bin/coordinator-frontend/src/components/WalletConnection.tsx deleted file mode 100644 index f0f30e4..0000000 --- a/bin/coordinator-frontend/src/components/WalletConnection.tsx +++ /dev/null @@ -1,79 +0,0 @@ -'use client'; - -import React from 'react'; -import { motion } from 'framer-motion'; -import { WalletMultiButton } from '@demox-labs/miden-wallet-adapter'; -import { useWallet } from '@demox-labs/miden-wallet-adapter'; -import { WalletAdapterNetwork } from '@demox-labs/miden-wallet-adapter-base'; -import { WalletConnectionProps } from '@/types'; - -export const WalletConnection: React.FC = ({ className = '' }) => { - const { connected, connecting, disconnecting } = useWallet(); - - return ( - -
-

- Wallet Connection -

-

- Connect your Miden wallet to manage your assets and transactions -

-
- -
- - - {connected && ( - -
- Connected -
- )} - - {(connecting || disconnecting) && ( - -
- - {connecting ? 'Connecting...' : 'Disconnecting...'} - -
- )} -
- - {connected && ( - -
-
- - Your wallet is connected and ready to use - -
-
- )} -
- ); -}; diff --git a/bin/coordinator-frontend/src/components/WalletInfo.tsx b/bin/coordinator-frontend/src/components/WalletInfo.tsx deleted file mode 100644 index 89785e8..0000000 --- a/bin/coordinator-frontend/src/components/WalletInfo.tsx +++ /dev/null @@ -1,86 +0,0 @@ -'use client'; - -import React from 'react'; -import { motion } from 'framer-motion'; -import { useWallet } from '@demox-labs/miden-wallet-adapter'; - -interface WalletInfoProps { - className?: string; -} - -export const WalletInfo: React.FC = ({ className = '' }) => { - const { connected, wallet, publicKey } = useWallet(); - - if (!connected || !wallet || !publicKey) { - return null; - } - - const formatPublicKey = (key: string) => { - if (key.length <= 12) return key; - return `${key.slice(0, 6)}...${key.slice(-6)}`; - }; - - return ( - -
-
-

- Wallet Information -

-
-
- Connected -
-
- -
-
- Wallet Name: - - {wallet.adapter.name} - -
- -
- Public Key: -
- - {formatPublicKey(publicKey.toString())} - - -
-
- -
- Network: - - Miden Testnet - -
-
- -
- -
-
-
- ); -}; diff --git a/bin/coordinator-frontend/src/components/WalletTransaction.tsx b/bin/coordinator-frontend/src/components/WalletTransaction.tsx deleted file mode 100644 index 6fbeb6f..0000000 --- a/bin/coordinator-frontend/src/components/WalletTransaction.tsx +++ /dev/null @@ -1,144 +0,0 @@ -'use client'; - -import React, { useState } from 'react'; -import { motion } from 'framer-motion'; -import { useWallet } from '@demox-labs/miden-wallet-adapter'; -import { useMidenSdk } from '../hooks/useMidenSdk'; - -interface WalletTransactionProps { - className?: string; -} - -export const WalletTransaction: React.FC = ({ className = '' }) => { - const { connected, publicKey } = useWallet(); - const { Miden, createClient, isLoading } = useMidenSdk(); - const [transactionStatus, setTransactionStatus] = useState(''); - const [isProcessing, setIsProcessing] = useState(false); - - const handleTestTransaction = async () => { - if (!connected || !publicKey || !Miden) { - setTransactionStatus('Please connect your wallet first'); - return; - } - - setIsProcessing(true); - setTransactionStatus('Initializing transaction...'); - - try { - const client = await createClient(); - if (!client) { - throw new Error('Failed to create client'); - } - - setTransactionStatus('Syncing client state...'); - await client.syncState(); - - setTransactionStatus('Transaction completed successfully!'); - - // Add a delay to show success message - setTimeout(() => { - setTransactionStatus(''); - }, 3000); - - } catch (error) { - console.error('Transaction error:', error); - setTransactionStatus(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`); - } finally { - setIsProcessing(false); - } - }; - - if (!connected) { - return ( - -
-
- - Please connect your wallet to perform transactions - -
-
- ); - } - - return ( - -
-
-

- Wallet Transaction -

-
-
- Ready -
-
- -
-
- Status: - - {isLoading ? 'Loading SDK...' : 'SDK Ready'} - -
- - {transactionStatus && ( - - - {transactionStatus} - - - )} -
- -
- -
-
-
- ); -}; diff --git a/bin/coordinator-frontend/src/config/api.ts b/bin/coordinator-frontend/src/config/api.ts deleted file mode 100644 index 08d56d1..0000000 --- a/bin/coordinator-frontend/src/config/api.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * API Configuration - * - * This configuration reads the backend coordinator API URL from environment variables. - * In Next.js, environment variables prefixed with NEXT_PUBLIC_ are exposed to the browser. - * - * Environment Variables: - * - NEXT_PUBLIC_COORDINATOR_API_URL: The backend coordinator API URL (defaults to http://localhost:59059) - */ - -const getCoordinatorApiUrl = (): string => { - // Check if we're in the browser - if (typeof window !== 'undefined') { - return process.env.NEXT_PUBLIC_EXTERNAL_COORDINATOR_API_URL || process.env.NEXT_PUBLIC_COORDINATOR_API_URL || 'http://localhost:59059'; - } - - return process.env.NEXT_PUBLIC_COORDINATOR_API_URL || 'http://localhost:59059'; -}; - -export const COORDINATOR_API_BASE_URL = getCoordinatorApiUrl(); - -if (process.env.NODE_ENV === 'development') { - console.log('API Configuration:', { - COORDINATOR_API_BASE_URL, - NEXT_PUBLIC_COORDINATOR_API_URL: process.env.NEXT_PUBLIC_COORDINATOR_API_URL, - NEXT_PUBLIC_EXTERNAL_COORDINATOR_API_URL: process.env.NEXT_PUBLIC_EXTERNAL_COORDINATOR_API_URL, - isServer: typeof window === 'undefined', - }); -} - diff --git a/bin/coordinator-frontend/src/config/psm.ts b/bin/coordinator-frontend/src/config/psm.ts new file mode 100644 index 0000000..cf31cb3 --- /dev/null +++ b/bin/coordinator-frontend/src/config/psm.ts @@ -0,0 +1,8 @@ +export const PSM_ENDPOINT = process.env.NEXT_PUBLIC_PSM_ENDPOINT || 'https://psm-stg.openzeppelin.com'; +export const MIDEN_RPC_URL = process.env.NEXT_PUBLIC_MIDEN_RPC_URL || 'https://rpc.devnet.miden.io'; +export const MIDEN_DB_NAME = 'MidenClientDB'; + +export const PARA_API_KEY = process.env.NEXT_PUBLIC_PARA_API_KEY || ''; +export const PARA_ENVIRONMENT = (process.env.NEXT_PUBLIC_PARA_ENVIRONMENT || 'development') as + | 'development' + | 'production'; diff --git a/bin/coordinator-frontend/src/contexts/MidenClientContext.tsx b/bin/coordinator-frontend/src/contexts/MidenClientContext.tsx deleted file mode 100644 index 49ac9c1..0000000 --- a/bin/coordinator-frontend/src/contexts/MidenClientContext.tsx +++ /dev/null @@ -1,93 +0,0 @@ -'use client'; - -import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; -import { MidenWebClientHandle } from '../../lib/miden-client'; - -interface MidenClientContextType { - handle: MidenWebClientHandle | null; - status: string; - isRunning: boolean; - isInitialized: boolean; - error: string | null; - reinitialize: () => Promise; -} - -const MidenClientContext = createContext(undefined); - -export function MidenClientProvider({ children }: { children: ReactNode }) { - const [handle, setHandle] = useState(null); - const [status, setStatus] = useState(''); - const [isRunning, setIsRunning] = useState(false); - const [isInitialized, setIsInitialized] = useState(false); - const [error, setError] = useState(null); - - const initializationRef = React.useRef(false); - - const reinitialize = async () => { - if (initializationRef.current || isRunning || isInitialized) { - console.info('MidenClient: Initialization skipped (already running or complete)'); - return; - } - - console.info('MidenClient: Starting initialization'); - initializationRef.current = true; - setIsRunning(true); - setStatus('Initializing Miden client...'); - setError(null); - - try { - const newHandle = new MidenWebClientHandle(); - setHandle(newHandle); - - const success = await newHandle.initialize(); - if (success) { - console.info('MidenClient: Initialized successfully'); - setStatus('Miden client initialized successfully!'); - setIsInitialized(true); - } else { - console.error('MidenClient: Initialization failed'); - setStatus('Failed to initialize Miden client'); - setIsInitialized(false); - setError('Initialization failed'); - initializationRef.current = false; // Allow retry on failure - } - } catch (err) { - const errorMessage = err instanceof Error ? err.message : 'Unknown error'; - console.error('MidenClient: Initialization error:', err); - setStatus(`Error: ${errorMessage}`); - setIsInitialized(false); - setError(errorMessage); - initializationRef.current = false; // Allow retry on error - } finally { - setIsRunning(false); - } - }; - - // Initialize once on mount - useEffect(() => { - reinitialize(); - }, []); - - return ( - - {children} - - ); -} - -export function useMidenClient() { - const context = useContext(MidenClientContext); - if (context === undefined) { - throw new Error('useMidenClient must be used within a MidenClientProvider'); - } - return context; -} diff --git a/bin/coordinator-frontend/src/contexts/MultisigContext.tsx b/bin/coordinator-frontend/src/contexts/MultisigContext.tsx new file mode 100644 index 0000000..66172bc --- /dev/null +++ b/bin/coordinator-frontend/src/contexts/MultisigContext.tsx @@ -0,0 +1,886 @@ +'use client'; + +import React, { createContext, useContext, useEffect, useRef, useState, useCallback, useMemo } from 'react'; +import { toast } from 'sonner'; + +import { + type Multisig, + type MultisigClient, + type AccountState, + type DetectedMultisigConfig, + type TransactionProposal, + type SignatureScheme, + type ProcedureThreshold, + type ParaSigningContext, +} from '@openzeppelin/miden-multisig-client'; +import { PsmHttpError } from '@openzeppelin/psm-client'; +import { WebClient } from '@miden-sdk/miden-sdk'; + +import { normalizeCommitment } from '@/lib/helpers'; +import { formatError, classifyWalletError } from '@/lib/errors'; +import { clearMidenDatabase, createWebClient, initializeSigner as initSigner, loadSignerKeys, saveSignerKeys } from '@/lib/initClient'; +import { + initMultisigClient, + createMultisigAccount, + loadMultisigAccount, + createSigner, +} from '@/lib/multisigApi'; +import type { ExternalSignerParams } from '@/lib/multisigApi'; +import { PSM_ENDPOINT } from '@/config/psm'; +import type { SignerInfo } from '@/types/psm'; +import type { WalletSource } from '@/wallets/types'; +import { useParaSession } from '@/hooks/useParaSession'; +import { useMidenWallet } from '@/hooks/useMidenWallet'; +import { MidenWalletAdapter } from '@demox-labs/miden-wallet-adapter-miden'; + +function isPendingCandidateError(error: unknown): boolean { + const errorStr = error instanceof Error ? error.message : String(error); + return ( + errorStr.includes('non-canonical delta pending') || + errorStr.includes('ConflictPendingDelta') + ); +} + +interface MultisigContextValue { + // Core state + webClient: WebClient | null; + multisigClient: MultisigClient | null; + signer: SignerInfo | null; + multisig: Multisig | null; + error: string | null; + pendingCandidateWarning: string | null; + + // PSM state + psmUrl: string; + psmStatus: 'connected' | 'connecting' | 'error'; + psmCommitment: string; + psmPublicKey: string | undefined; + psmState: AccountState | null; + + // Multisig data + detectedConfig: DetectedMultisigConfig | null; + proposals: TransactionProposal[]; + consumableNotes: Array<{ id: string; assets: Array<{ faucetId: string; amount: bigint }> }>; + + // Wallet state + walletSource: WalletSource; + activeCommitment: string | null; + activeScheme: SignatureScheme; + paraSession: { connected: boolean; commitment: string | null; publicKey: string | null }; + midenWalletSession: { connected: boolean; commitment: string | null }; + + // Loading flags + creating: boolean; + registeringOnPsm: boolean; + loadingAccount: boolean; + syncingState: boolean; + creatingProposal: boolean; + signingProposal: string | null; + executingProposal: string | null; + generatingSigner: boolean; + + // Operations + handleCreate: ( + otherSignerCommitments: string[], + threshold: number, + procedureThresholds?: ProcedureThreshold[], + signatureScheme?: SignatureScheme, + ) => Promise; + handleLoad: (accountId: string, signatureScheme?: SignatureScheme) => Promise; + handleSync: () => Promise; + handleSignProposal: (proposalId: string) => Promise; + handleExecuteProposal: (proposalId: string) => Promise; + handleCreateSendProposal: (recipientId: string, faucetId: string, amount: bigint) => Promise; + handleCreateConsumeNotesProposal: (noteIds: string[]) => Promise; + handleCreateAddSignerProposal: (commitment: string, increaseThreshold: boolean) => Promise; + handleCreateRemoveSignerProposal: (signerToRemove: string, newThreshold?: number) => Promise; + handleCreateChangeThresholdProposal: (newThreshold: number) => Promise; + handleCreateSwitchPsmProposal: (newEndpoint: string, newPubkey: string) => Promise; + handleExportProposal: (proposalId: string) => void; + handleSignProposalOffline: (proposalId: string) => Promise; + handleImportProposal: (json: string) => void; + handleDisconnect: () => void; + setWalletSource: (source: WalletSource) => void; + setPsmUrl: (url: string) => void; + connectToPsm: (url: string) => Promise; + dismissWarning: () => void; + setError: (error: string | null) => void; + + // Wallet actions + connectMidenWallet: () => Promise; + disconnectMidenWallet: () => Promise; + openParaModal: () => void; + paraModalOpen: boolean; + closeParaModal: () => void; +} + +const MultisigContext = createContext(null); + +export function useMultisig(): MultisigContextValue { + const ctx = useContext(MultisigContext); + if (!ctx) throw new Error('useMultisig must be used within MultisigProvider'); + return ctx; +} + +export function MultisigProvider({ children }: { children: React.ReactNode }) { + const [webClient, setWebClient] = useState(null); + const [multisigClient, setMultisigClient] = useState(null); + const [signer, setSigner] = useState(null); + const [generatingSigner, setGeneratingSigner] = useState(false); + const [multisig, setMultisig] = useState(null); + const [error, setError] = useState(null); + const [pendingCandidateWarning, setPendingCandidateWarning] = useState(null); + + const [psmUrl, setPsmUrl] = useState(PSM_ENDPOINT); + const [psmStatus, setPsmStatus] = useState<'connected' | 'connecting' | 'error'>('connecting'); + const [psmCommitment, setPsmCommitment] = useState(''); + const [psmPublicKey, setPsmPublicKey] = useState(undefined); + const [psmState, setPsmState] = useState(null); + + const [creating, setCreating] = useState(false); + const [registeringOnPsm, setRegisteringOnPsm] = useState(false); + const [loadingAccount, setLoadingAccount] = useState(false); + const [detectedConfig, setDetectedConfig] = useState(null); + const [syncingState, setSyncingState] = useState(false); + + const [proposals, setProposals] = useState([]); + const [creatingProposal, setCreatingProposal] = useState(false); + const [signingProposal, setSigningProposal] = useState(null); + const [executingProposal, setExecutingProposal] = useState(null); + + const [consumableNotes, setConsumableNotes] = useState }>>([]); + + const [walletSource, setWalletSource] = useState('local'); + const [paraModalOpen, setParaModalOpen] = useState(false); + + const { session: paraSession, paraClient, getWalletId } = useParaSession(); + const [midenWalletAdapter] = useState(() => new MidenWalletAdapter({ appName: 'Miden Multisig' })); + const { session: midenWalletSession, connect: connectMidenWalletRaw, disconnect: disconnectMidenWallet, signBytes, connectError: midenWalletConnectError } = useMidenWallet(midenWalletAdapter); + + // Show Miden Wallet connection errors + useEffect(() => { + if (midenWalletConnectError) { + toast.error(midenWalletConnectError); + } + }, [midenWalletConnectError]); + + // Auto-switch wallet source when an external wallet connects + useEffect(() => { + if (paraSession.connected) { + setWalletSource('para'); + if (paraModalOpen) setParaModalOpen(false); + } + }, [paraSession.connected, paraModalOpen]); + + useEffect(() => { + if (midenWalletSession.connected) { + setWalletSource('miden-wallet'); + } + }, [midenWalletSession.connected]); + + const activeCommitment = useMemo(() => { + if (walletSource === 'para' && paraSession.connected) return paraSession.commitment; + if (walletSource === 'miden-wallet' && midenWalletSession.connected) return midenWalletSession.commitment; + if (!signer) return null; + return signer.activeScheme === 'ecdsa' ? signer.ecdsa.commitment : signer.falcon.commitment; + }, [walletSource, paraSession, midenWalletSession, signer]); + + const activeScheme = useMemo((): SignatureScheme => { + if (walletSource === 'para') return 'ecdsa'; + if (walletSource === 'miden-wallet' && midenWalletSession.scheme) return midenWalletSession.scheme; + return signer?.activeScheme ?? 'falcon'; + }, [walletSource, midenWalletSession, signer]); + + const buildExternalParams = useCallback((): ExternalSignerParams | undefined => { + if (walletSource === 'para' && paraSession.connected && paraClient) { + const walletId = getWalletId(); + if (!walletId || !paraSession.commitment || !paraSession.publicKey) return undefined; + return { + walletSource: 'para', + paraContext: { + para: paraClient as ParaSigningContext, + walletId, + commitment: paraSession.commitment, + publicKey: paraSession.publicKey, + }, + }; + } + if (walletSource === 'miden-wallet' && midenWalletSession.connected) { + if (!midenWalletSession.commitment || !midenWalletSession.scheme) return undefined; + return { + walletSource: 'miden-wallet', + midenWalletContext: { + wallet: { signBytes }, + commitment: midenWalletSession.commitment, + scheme: midenWalletSession.scheme, + }, + }; + } + return undefined; + }, [walletSource, paraSession, paraClient, getWalletId, midenWalletSession, signBytes]); + + const connectToPsm = useCallback( + async (url: string, client?: WebClient): Promise => { + setPsmStatus('connecting'); + setError(null); + try { + const wc = client ?? webClient; + if (!wc) { + const response = await fetch(`${url}/pubkey`); + const data = await response.json(); + setPsmCommitment(data.commitment ?? ''); + setPsmPublicKey(data.pubkey); + setPsmStatus('connected'); + return; + } + + const { client: msClient, psmCommitment: commitment, psmPubkey: pubkey } = + await initMultisigClient(wc, url); + setPsmCommitment(commitment); + setPsmPublicKey(pubkey); + setMultisigClient(msClient); + setPsmStatus('connected'); + + if (multisig && signer && psmState?.stateDataBase64) { + setRegisteringOnPsm(true); + try { + let ackPublicKey = pubkey; + if (signer.activeScheme === 'ecdsa' && !ackPublicKey) { + const { pubkey: fetched } = await msClient.psmClient.getPubkey('ecdsa'); + ackPublicKey = fetched; + setPsmPublicKey(fetched); + } + const clientSigner = createSigner(signer, signer.activeScheme, buildExternalParams()); + const reloadedMs = await loadMultisigAccount( + msClient, + multisig.accountId, + clientSigner, + ackPublicKey, + ); + setMultisig(reloadedMs); + + const { proposals: synced, state, notes, config } = await reloadedMs.syncAll(); + setPsmState(state); + setDetectedConfig(config); + setProposals(synced); + setConsumableNotes(notes); + + toast.success('Account loaded from PSM'); + } catch (loadErr) { + const isNotFound = loadErr instanceof PsmHttpError && loadErr.status === 404; + const isNonceTooLow = loadErr instanceof Error && loadErr.message.includes('nonce') && loadErr.message.includes('too low'); + + if (isNotFound || isNonceTooLow) { + try { + await multisig.switchPsm(msClient.psmClient); + + const { proposals: synced, state, notes, config } = await multisig.syncAll(); + setPsmState(state); + setDetectedConfig(config); + setProposals(synced); + setConsumableNotes(notes); + + toast.success('Account registered on new PSM'); + } catch (registerErr) { + setError(`Failed to register account on new PSM: ${formatError(registerErr)}`); + } + } else { + setError(`Failed to load account from PSM: ${formatError(loadErr)}`); + } + } finally { + setRegisteringOnPsm(false); + } + } + } catch (err) { + const msg = formatError(err); + setPsmStatus('error'); + setPsmCommitment(''); + setPsmPublicKey(undefined); + setError(`Failed to connect to PSM: ${msg}`); + } + }, + [webClient, multisig, signer, psmState, buildExternalParams] + ); + + // Initialization + useEffect(() => { + const init = async () => { + try { + await clearMidenDatabase(); + + const client = await createWebClient(); + setWebClient(client); + + await connectToPsm(psmUrl, client); + + setGeneratingSigner(true); + let signerInfo = await loadSignerKeys(); + if (!signerInfo) { + signerInfo = initSigner(); + await saveSignerKeys(signerInfo); + } + setSigner(signerInfo); + } catch (err) { + setError(formatError(err, 'Initialization failed')); + } finally { + setGeneratingSigner(false); + } + }; + init(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const handleCreate = useCallback(async ( + otherSignerCommitments: string[], + threshold: number, + procedureThresholds?: ProcedureThreshold[], + signatureScheme: SignatureScheme = 'falcon', + ) => { + if (!multisigClient || !signer || !psmCommitment) { + setError('Client not initialized. Try reconnecting to PSM.'); + return; + } + + setCreating(true); + setError(null); + try { + setSigner((prev) => (prev ? { ...prev, activeScheme: signatureScheme } : prev)); + let ackPublicKey = psmPublicKey; + let accountPsmCommitment = psmCommitment; + if (signatureScheme === 'ecdsa') { + const { pubkey, commitment } = await multisigClient.psmClient.getPubkey('ecdsa'); + if (!ackPublicKey) { + ackPublicKey = pubkey; + setPsmPublicKey(pubkey); + } + accountPsmCommitment = commitment; + setPsmCommitment(commitment); + } + + const externalParams = buildExternalParams(); + const clientSigner = createSigner(signer, signatureScheme, externalParams); + const signerCommitment = externalParams?.paraContext?.commitment + ?? externalParams?.midenWalletContext?.commitment + ?? (signatureScheme === 'ecdsa' ? signer.ecdsa.commitment : signer.falcon.commitment); + + const ms = await createMultisigAccount( + multisigClient, + signerCommitment, + otherSignerCommitments, + threshold, + accountPsmCommitment, + clientSigner, + ackPublicKey, + procedureThresholds, + signatureScheme + ); + setMultisig(ms); + + // Persist account ID so middleware allows dashboard access + if (ms.accountId) { + localStorage.setItem('currentWalletId', ms.accountId); + document.cookie = `currentWalletId=${ms.accountId}; path=/; max-age=31536000`; + } + + setRegisteringOnPsm(true); + try { + await ms.registerOnPsm(); + const { proposals: synced, state, notes, config } = await ms.syncAll(); + setDetectedConfig(config); + setPsmState(state); + setProposals(synced); + setConsumableNotes(notes); + } catch (psmErr) { + setError(`Created but failed to register on PSM: ${psmErr instanceof Error ? psmErr.message : 'Unknown'}`); + } finally { + setRegisteringOnPsm(false); + } + } catch (err) { + if (walletSource !== 'local') { + setError(classifyWalletError(err)); + } else { + setError(formatError(err, 'Failed to create')); + } + } finally { + setCreating(false); + } + }, [multisigClient, signer, psmCommitment, psmPublicKey, walletSource, buildExternalParams]); + + const handleLoad = useCallback(async (accountId: string, signatureScheme: SignatureScheme = 'falcon') => { + if (!multisigClient || !signer) { + setError('Client not initialized. Try reconnecting to PSM.'); + return; + } + if (!psmCommitment) { + setPsmStatus('error'); + setError('Not connected to PSM. Check the endpoint and try again.'); + return; + } + + let normalizedId = accountId; + if (!normalizedId.startsWith('0x')) { + normalizedId = `0x${normalizedId}`; + } + + setLoadingAccount(true); + setError(null); + setDetectedConfig(null); + try { + setSigner((prev) => (prev ? { ...prev, activeScheme: signatureScheme } : prev)); + let ackPublicKey = psmPublicKey; + if (signatureScheme === 'ecdsa' && !ackPublicKey) { + const { pubkey } = await multisigClient.psmClient.getPubkey('ecdsa'); + ackPublicKey = pubkey; + setPsmPublicKey(pubkey); + } + + const externalParams = buildExternalParams(); + const clientSigner = createSigner(signer, signatureScheme, externalParams); + + const ms = await loadMultisigAccount( + multisigClient, + normalizedId, + clientSigner, + ackPublicKey, + ); + setMultisig(ms); + + // Persist account ID so middleware allows dashboard access + if (ms.accountId) { + localStorage.setItem('currentWalletId', ms.accountId); + document.cookie = `currentWalletId=${ms.accountId}; path=/; max-age=31536000`; + } + + const { proposals: synced, state, notes, config } = await ms.syncAll(); + setDetectedConfig(config); + setPsmState(state); + setProposals(synced); + setConsumableNotes(notes); + } catch (err) { + const message = err instanceof Error ? err.message : 'Unknown'; + if (message.includes('404') || message.includes('not found')) { + setError('Account not found on PSM'); + } else { + setError(`Failed to load: ${message}`); + } + } finally { + setLoadingAccount(false); + } + }, [multisigClient, signer, psmCommitment, psmPublicKey, buildExternalParams]); + + // Auto-load saved account after initialization completes + const autoLoadAttemptedRef = useRef(false); + useEffect(() => { + if (autoLoadAttemptedRef.current) return; + if (!multisigClient || !signer || !psmCommitment) return; + const savedId = localStorage.getItem('currentWalletId'); + if (!savedId) return; + autoLoadAttemptedRef.current = true; + handleLoad(savedId); + }, [multisigClient, signer, psmCommitment, handleLoad]); + + const handleSync = useCallback(async () => { + if (!multisig || !webClient) return; + + setSyncingState(true); + setError(null); + setPendingCandidateWarning(null); + try { + try { + await webClient.syncState(); + } catch { + await new Promise(resolve => setTimeout(resolve, 500)); + await webClient.syncState(); + } + + const { proposals: synced, state, notes, config } = await multisig.syncAll(); + setPsmState(state); + setDetectedConfig(config); + setProposals(synced); + setConsumableNotes(notes); + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + if (message.includes('account nonce is too low to import')) { + setPendingCandidateWarning( + 'Sync warning: local state is ahead of the on-chain state. ' + + 'This can happen right after executing a transaction. Please wait a moment and sync again.' + ); + setError(null); + } else { + setError(formatError(err, 'Sync failed')); + } + } finally { + setSyncingState(false); + } + }, [multisig, webClient]); + + const handleCreateAddSignerProposal = useCallback(async (commitment: string, increaseThreshold: boolean) => { + if (!multisig) return; + + let normalizedCommitment: string; + try { + normalizedCommitment = normalizeCommitment(commitment); + } catch (e: unknown) { + setError(e instanceof Error ? e.message : 'Invalid commitment'); + return; + } + + setCreatingProposal(true); + setError(null); + setPendingCandidateWarning(null); + try { + const newThreshold = increaseThreshold ? multisig.threshold + 1 : undefined; + const { proposals } = await multisig.createAddSignerProposal(normalizedCommitment, { newThreshold }); + setProposals(proposals); + toast.success('Add signer proposal created'); + } catch (err) { + if (isPendingCandidateError(err)) { + setPendingCandidateWarning( + 'A previous transaction is still being processed on-chain. ' + + 'Please wait for it to be confirmed before creating new proposals.' + ); + } else { + setError(`Failed to create proposal: ${err instanceof Error ? err.message : 'Unknown'}`); + } + } finally { + setCreatingProposal(false); + } + }, [multisig]); + + const handleCreateRemoveSignerProposal = useCallback(async (signerToRemove: string, newThreshold?: number) => { + if (!multisig) return; + + setCreatingProposal(true); + setError(null); + setPendingCandidateWarning(null); + try { + const { proposals } = await multisig.createRemoveSignerProposal(signerToRemove, { newThreshold }); + setProposals(proposals); + toast.success('Remove signer proposal created'); + } catch (err) { + if (isPendingCandidateError(err)) { + setPendingCandidateWarning( + 'A previous transaction is still being processed on-chain. ' + + 'Please wait for it to be confirmed before creating new proposals.' + ); + } else { + setError(`Failed to create proposal: ${err instanceof Error ? err.message : 'Unknown'}`); + } + } finally { + setCreatingProposal(false); + } + }, [multisig]); + + const handleCreateChangeThresholdProposal = useCallback(async (newThreshold: number) => { + if (!multisig) return; + + setCreatingProposal(true); + setError(null); + setPendingCandidateWarning(null); + try { + const { proposals } = await multisig.createChangeThresholdProposal(newThreshold); + setProposals(proposals); + toast.success('Change threshold proposal created'); + } catch (err) { + if (isPendingCandidateError(err)) { + setPendingCandidateWarning( + 'A previous transaction is still being processed on-chain. ' + + 'Please wait for it to be confirmed before creating new proposals.' + ); + } else { + setError(`Failed to create proposal: ${err instanceof Error ? err.message : 'Unknown'}`); + } + } finally { + setCreatingProposal(false); + } + }, [multisig]); + + const handleCreateConsumeNotesProposal = useCallback(async (noteIds: string[]) => { + if (!multisig) return; + + setCreatingProposal(true); + setError(null); + setPendingCandidateWarning(null); + try { + const { proposals } = await multisig.createConsumeNotesProposal(noteIds); + setProposals(proposals); + toast.success('Consume notes proposal created'); + } catch (err) { + if (isPendingCandidateError(err)) { + setPendingCandidateWarning( + 'A previous transaction is still being processed on-chain. ' + + 'Please wait for it to be confirmed before creating new proposals.' + ); + } else { + setError(`Failed to create proposal: ${err instanceof Error ? err.message : 'Unknown'}`); + } + } finally { + setCreatingProposal(false); + } + }, [multisig]); + + const handleCreateSendProposal = useCallback(async (recipientId: string, faucetId: string, amount: bigint) => { + if (!multisig) return; + + setCreatingProposal(true); + setError(null); + setPendingCandidateWarning(null); + try { + const { proposals } = await multisig.createSendProposal(recipientId, faucetId, amount); + setProposals(proposals); + toast.success('Send payment proposal created'); + } catch (err) { + if (isPendingCandidateError(err)) { + setPendingCandidateWarning( + 'A previous transaction is still being processed on-chain. ' + + 'Please wait for it to be confirmed before creating new proposals.' + ); + } else { + setError(`Failed to create proposal: ${err instanceof Error ? err.message : 'Unknown'}`); + } + } finally { + setCreatingProposal(false); + } + }, [multisig]); + + const handleCreateSwitchPsmProposal = useCallback(async (newEndpoint: string, newPubkey: string) => { + if (!multisig) return; + + setCreatingProposal(true); + setError(null); + setPendingCandidateWarning(null); + try { + const { proposals } = await multisig.createSwitchPsmProposal(newEndpoint, newPubkey); + setProposals(proposals); + toast.success('Switch PSM proposal created'); + } catch (err) { + if (isPendingCandidateError(err)) { + setPendingCandidateWarning( + 'A previous transaction is still being processed on-chain. ' + + 'Please wait for it to be confirmed before creating new proposals.' + ); + } else { + setError(`Failed to create proposal: ${err instanceof Error ? err.message : 'Unknown'}`); + } + } finally { + setCreatingProposal(false); + } + }, [multisig]); + + const handleSignProposal = useCallback(async (proposalId: string) => { + if (!multisig) return; + + setSigningProposal(proposalId); + setError(null); + try { + const proposals = await multisig.signTransactionProposal(proposalId); + setProposals(proposals); + } catch (err) { + if (walletSource !== 'local') { + setError(classifyWalletError(err)); + } else { + setError(`Failed to sign: ${err instanceof Error ? err.message : 'Unknown'}`); + } + } finally { + setSigningProposal(null); + } + }, [multisig, walletSource]); + + const handleExecuteProposal = useCallback(async (proposalId: string) => { + if (!multisig) return; + + setExecutingProposal(proposalId); + setError(null); + setPendingCandidateWarning(null); + try { + await multisig.executeTransactionProposal(proposalId); + toast.success('Proposal executed successfully'); + + // Sync after execution + if (webClient) { + setSyncingState(true); + try { + try { + await webClient.syncState(); + } catch { + await new Promise(resolve => setTimeout(resolve, 500)); + await webClient.syncState(); + } + const { proposals: synced, state, notes, config } = await multisig.syncAll(); + setPsmState(state); + setDetectedConfig(config); + setProposals(synced); + setConsumableNotes(notes); + } catch (syncErr) { + const message = syncErr instanceof Error ? syncErr.message : String(syncErr); + if (message.includes('account nonce is too low to import')) { + setPendingCandidateWarning( + 'Sync warning: local state is ahead of the on-chain state. ' + + 'This can happen right after executing a transaction. Please wait a moment and sync again.' + ); + } + } finally { + setSyncingState(false); + } + } + } catch (err) { + const message = formatError(err, 'Execute failed'); + if (isPendingCandidateError(err)) { + setPendingCandidateWarning( + 'A previous transaction is still being processed on-chain. ' + + 'Please wait for it to be confirmed before executing proposals.' + ); + } else { + setError(message); + toast.error(message); + } + } finally { + setExecutingProposal(null); + } + }, [multisig, webClient]); + + const handleExportProposal = useCallback((proposalId: string) => { + if (!multisig) return; + + try { + const json = multisig.exportTransactionProposalToJson(proposalId); + navigator.clipboard.writeText(json); + toast.success('Proposal JSON copied to clipboard'); + } catch (err) { + setError(`Failed to export: ${err instanceof Error ? err.message : 'Unknown'}`); + } + }, [multisig]); + + const handleSignProposalOffline = useCallback(async (proposalId: string) => { + if (!multisig) return; + + try { + const json = await multisig.signTransactionProposalOffline(proposalId); + navigator.clipboard.writeText(json); + setProposals(multisig.listTransactionProposals()); + toast.success('Signed! Updated proposal JSON copied to clipboard'); + } catch (err) { + setError(`Failed to sign offline: ${err instanceof Error ? err.message : 'Unknown'}`); + } + }, [multisig]); + + const handleImportProposal = useCallback((json: string) => { + if (!multisig || !json.trim()) return; + + try { + const { proposal, proposals } = multisig.importTransactionProposal(json.trim()); + setProposals(proposals); + toast.success(`Proposal imported: ${proposal.id.slice(0, 12)}...`); + } catch (err) { + setError(`Failed to import: ${err instanceof Error ? err.message : 'Unknown'}`); + } + }, [multisig]); + + const handleDisconnect = useCallback(() => { + setMultisig(null); + setPsmState(null); + setProposals([]); + setError(null); + setDetectedConfig(null); + setConsumableNotes([]); + }, []); + + const connectMidenWallet = useCallback(async () => { + try { + await connectMidenWalletRaw(); + } catch (err) { + toast.error(classifyWalletError(err)); + } + }, [connectMidenWalletRaw]); + + const value = useMemo((): MultisigContextValue => ({ + webClient, + multisigClient, + signer, + multisig, + error, + pendingCandidateWarning, + + psmUrl, + psmStatus, + psmCommitment, + psmPublicKey, + psmState, + + detectedConfig, + proposals, + consumableNotes, + + walletSource, + activeCommitment, + activeScheme, + paraSession: { + connected: paraSession.connected, + commitment: paraSession.commitment, + publicKey: paraSession.publicKey, + }, + midenWalletSession: { + connected: midenWalletSession.connected, + commitment: midenWalletSession.commitment, + }, + + creating, + registeringOnPsm, + loadingAccount, + syncingState, + creatingProposal, + signingProposal, + executingProposal, + generatingSigner, + + handleCreate, + handleLoad, + handleSync, + handleSignProposal, + handleExecuteProposal, + handleCreateSendProposal, + handleCreateConsumeNotesProposal, + handleCreateAddSignerProposal, + handleCreateRemoveSignerProposal, + handleCreateChangeThresholdProposal, + handleCreateSwitchPsmProposal, + handleExportProposal, + handleSignProposalOffline, + handleImportProposal, + handleDisconnect, + setWalletSource, + setPsmUrl, + connectToPsm, + dismissWarning: () => setPendingCandidateWarning(null), + setError, + + connectMidenWallet, + disconnectMidenWallet, + openParaModal: () => setParaModalOpen(true), + paraModalOpen, + closeParaModal: () => setParaModalOpen(false), + }), [ + webClient, multisigClient, signer, multisig, error, pendingCandidateWarning, + psmUrl, psmStatus, psmCommitment, psmPublicKey, psmState, + detectedConfig, proposals, consumableNotes, + walletSource, activeCommitment, activeScheme, + paraSession.connected, paraSession.commitment, paraSession.publicKey, + midenWalletSession.connected, midenWalletSession.commitment, + creating, registeringOnPsm, loadingAccount, syncingState, + creatingProposal, signingProposal, executingProposal, generatingSigner, + handleCreate, handleLoad, handleSync, + handleSignProposal, handleExecuteProposal, + handleCreateSendProposal, handleCreateConsumeNotesProposal, + handleCreateAddSignerProposal, handleCreateRemoveSignerProposal, + handleCreateChangeThresholdProposal, handleCreateSwitchPsmProposal, + handleExportProposal, handleSignProposalOffline, handleImportProposal, + handleDisconnect, connectToPsm, + connectMidenWallet, disconnectMidenWallet, paraModalOpen, + ]); + + return ( + + {children} + + ); +} diff --git a/bin/coordinator-frontend/src/hooks/useFungibleAssets.ts b/bin/coordinator-frontend/src/hooks/useFungibleAssets.ts deleted file mode 100644 index c97e99d..0000000 --- a/bin/coordinator-frontend/src/hooks/useFungibleAssets.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { useState, useEffect, useCallback } from 'react'; -import { useMidenSdk } from './useMidenSdk'; -import { useMidenClient } from '../contexts/MidenClientContext'; -import { AccountId, NetworkId, AccountInterface } from '@demox-labs/miden-sdk'; -import { FungibleAsset } from '@/types'; - -export const useFungibleAssets = () => { - const { Miden } = useMidenSdk(); - const { handle, isInitialized } = useMidenClient(); - const [fungibleAssets, setFungibleAssets] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - const getFungibleAssets = useCallback(async () => { - try { - setIsLoading(true); - setError(null); - - const currentWalletId = localStorage.getItem("currentWalletId"); - if (!currentWalletId || !Miden || !handle || !isInitialized) { - setIsLoading(false); - return; - } - - const webClient = handle.getWebClient(); - if (!webClient) { - setIsLoading(false); - return; - } - - const address = Miden.Address.fromBech32(currentWalletId); - const accountId = address.accountId(); - - // Sync state before getting/importing account - await webClient.syncState(); - - let account = await webClient.getAccount(accountId); - if (!account) { - await webClient.importAccountById(accountId); - account = await webClient.getAccount(accountId); - } - - if (account) { - const assetVault = account.vault(); - const assets = assetVault.fungibleAssets(); - - const assetsWithBalance = assets.map(asset => { - const faucetId = asset.faucetId().toBech32(NetworkId.Testnet, AccountInterface.BasicWallet); - const balance = assetVault.getBalance(asset.faucetId()).toString(); - return { faucetId, balance }; - }); - - setFungibleAssets(assetsWithBalance); - } - } catch (error) { - console.error('Error getting fungible assets:', error); - setError(error instanceof Error ? error.message : 'Failed to fetch fungible assets'); - } finally { - setIsLoading(false); - } - }, [Miden, handle, isInitialized]); - - // Load fungible assets on component mount - useEffect(() => { - getFungibleAssets(); - }, [getFungibleAssets]); - - return { - fungibleAssets, - isLoading, - error, - getFungibleAssets - }; -}; diff --git a/bin/coordinator-frontend/src/hooks/useMidenSdk.tsx b/bin/coordinator-frontend/src/hooks/useMidenSdk.tsx deleted file mode 100644 index 6cedf6d..0000000 --- a/bin/coordinator-frontend/src/hooks/useMidenSdk.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { - createContext, - FC, - useCallback, - useContext, - useEffect, - useState, -} from 'react'; - -import { MidenSdkContextState, MidenSdkProviderProps } from '@/types'; - -// Type-only import to avoid loading WASM during build -type MidenSdkType = typeof import('@demox-labs/miden-sdk'); - -const defaultContext: { - isLoading: boolean; - Miden: MidenSdkType | null; -} = { - isLoading: true, - Miden: null, -}; - -export const MidenSdkContext = createContext( - defaultContext as MidenSdkContextState -); - -export const useMidenSdk = (): MidenSdkContextState => { - return useContext(MidenSdkContext); -}; - -export const MidenSdkProvider: FC = ({ children }) => { - const [isLoading, setIsLoading] = useState(true); - const [Miden, setMiden] = useState(null); - - const loadSdk = useCallback(async () => { - if (!isLoading && Miden !== null) return; - const sdk: typeof import('@demox-labs/miden-sdk') = await import( - '@demox-labs/miden-sdk' - ); - setIsLoading(false); - setMiden(sdk); - }, [isLoading, Miden, setIsLoading, setMiden]); - - const createClient = useCallback(async () => { - if (!Miden) return null; - return await Miden.WebClient.createClient('https://rpc.testnet.miden.io'); - }, [Miden]); - - useEffect(() => { - loadSdk(); - }, [loadSdk]); - - return ( - - {children} - - ); -}; diff --git a/bin/coordinator-frontend/src/hooks/useMidenWallet.ts b/bin/coordinator-frontend/src/hooks/useMidenWallet.ts new file mode 100644 index 0000000..280beb7 --- /dev/null +++ b/bin/coordinator-frontend/src/hooks/useMidenWallet.ts @@ -0,0 +1,104 @@ +'use client'; + +import { useState, useEffect, useRef, useCallback } from 'react'; +import type { MessageSignerWalletAdapter } from '@demox-labs/miden-wallet-adapter-base'; +import { + WalletAdapterNetwork, + PrivateDataPermission, +} from '@demox-labs/miden-wallet-adapter-base'; +import { PublicKeyFormat } from '@openzeppelin/miden-multisig-client'; +import type { ExternalWalletState } from '@/wallets/types'; + +export function useMidenWallet(adapter: MessageSignerWalletAdapter | null) { + const [session, setSession] = useState({ + source: 'miden-wallet', + connected: false, + publicKey: null, + commitment: null, + scheme: null, + }); + const [connectError, setConnectError] = useState(null); + const connectingRef = useRef(false); + + useEffect(() => { + if (!adapter) return; + + const handleConnect = (_address: string) => { + const pk = adapter.publicKey; + if (!pk) { + setConnectError('Miden Wallet connected but did not provide a public key'); + return; + } + const { scheme, publicKeyHex, commitment } = PublicKeyFormat.parse(pk); + if (!commitment) { + setConnectError(`Failed to derive commitment from ${scheme} public key (len=${pk.length})`); + return; + } + setConnectError(null); + setSession({ + source: 'miden-wallet', + connected: true, + publicKey: publicKeyHex, + commitment, + scheme, + }); + }; + + const handleDisconnect = () => { + setConnectError(null); + setSession((prev) => ({ + ...prev, + connected: false, + publicKey: null, + commitment: null, + scheme: null, + })); + }; + + const handleError = (err: Error) => { + setConnectError(err.message || err.name || 'Unknown wallet error'); + }; + + adapter.on('connect', handleConnect); + adapter.on('disconnect', handleDisconnect); + adapter.on('error', handleError); + + if (adapter.connected && adapter.address) { + handleConnect(adapter.address); + } + + return () => { + adapter.off('connect', handleConnect); + adapter.off('disconnect', handleDisconnect); + adapter.off('error', handleError); + }; + }, [adapter]); + + const connect = useCallback(async () => { + if (!adapter || connectingRef.current) return; + connectingRef.current = true; + try { + await adapter.connect( + PrivateDataPermission.UponRequest, + WalletAdapterNetwork.Testnet, + ); + } finally { + connectingRef.current = false; + } + }, [adapter]); + + const disconnect = useCallback(async () => { + if (!adapter) return; + await adapter.disconnect(); + }, [adapter]); + + const signBytes = useCallback( + async (data: Uint8Array, kind: 'word' | 'signingInputs'): Promise => { + if (!adapter) throw new Error('Miden Wallet not connected'); + return adapter.signBytes(data, kind); + }, + [adapter], + ); + + return { session, connect, disconnect, signBytes, connectError }; +} diff --git a/bin/coordinator-frontend/src/hooks/useParaSession.ts b/bin/coordinator-frontend/src/hooks/useParaSession.ts new file mode 100644 index 0000000..f36eb86 --- /dev/null +++ b/bin/coordinator-frontend/src/hooks/useParaSession.ts @@ -0,0 +1,93 @@ +'use client'; + +import { useState, useCallback, useEffect, useRef } from 'react'; +import { useParaMiden } from '@miden-sdk/use-miden-para-react'; +import { tryComputeEcdsaCommitmentHex, EcdsaFormat } from '@openzeppelin/miden-multisig-client'; +import { MIDEN_RPC_URL } from '@/config/psm'; +import type { ExternalWalletState } from '@/wallets/types'; + +interface WalletWithPublicKey { + id: string; + publicKey?: string; +} + +async function getUncompressedPublicKeyFromWallet( + para: { issueJwt(): Promise<{ token: string }> }, + wallet: WalletWithPublicKey, +): Promise { + let publicKey = wallet.publicKey; + if (!publicKey) { + const { token } = await para.issueJwt(); + const payload = JSON.parse(window.atob(token.split('.')[1])); + if (!payload.data) { + throw new Error('Got invalid jwt token'); + } + const wallets: Array<{ id: string; publicKey: string }> = payload.data.connectedWallets; + const w = wallets.find((w) => w.id === wallet.id); + if (!w) { + throw new Error('Wallet Not Found in jwt data'); + } + publicKey = w.publicKey; + } + return publicKey; +} + +export function useParaSession() { + const [session, setSession] = useState({ + source: 'para', + connected: false, + publicKey: null, + commitment: null, + scheme: null, + }); + + const paraMiden = useParaMiden(MIDEN_RPC_URL, 'public', {}, false); + const derivingRef = useRef(false); + + const { para: paraClient, evmWallets } = paraMiden; + + useEffect(() => { + if (!evmWallets?.length) { + setSession((prev) => ({ ...prev, connected: false, publicKey: null, commitment: null })); + return; + } + + const evmWallet = evmWallets[0]; + if (!paraClient || derivingRef.current) return; + + derivingRef.current = true; + (async () => { + try { + const uncompressedPk = await getUncompressedPublicKeyFromWallet(paraClient, evmWallet); + const compressedPk = EcdsaFormat.compressPublicKey(uncompressedPk); + const commitmentHex = tryComputeEcdsaCommitmentHex(compressedPk); + if (!commitmentHex) { + throw new Error('Failed to derive ECDSA commitment from public key'); + } + + setSession({ + source: 'para', + connected: true, + publicKey: uncompressedPk, + commitment: commitmentHex, + scheme: 'ecdsa', + }); + } catch { + setSession((prev) => ({ ...prev, connected: false })); + } finally { + derivingRef.current = false; + } + })(); + }, [evmWallets, paraClient]); + + const getWalletId = useCallback((): string | null => { + return evmWallets?.[0]?.id ?? null; + }, [evmWallets]); + + return { + session, + paraClient, + paraMiden, + getWalletId, + }; +} diff --git a/bin/coordinator-frontend/src/hooks/useWalletData.ts b/bin/coordinator-frontend/src/hooks/useWalletData.ts deleted file mode 100644 index 5f8ba58..0000000 --- a/bin/coordinator-frontend/src/hooks/useWalletData.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { fetchApproverListThunk, fetchMultisigAccountDetailsThunk } from '../services/transactionApi'; -import { WalletFormData, WalletData } from '../types'; -import { AppDispatch, RootState } from '../store'; - -export const useWalletData = () => { - const dispatch = useDispatch(); - const { approvers, walletData: walletDataFromStore } = useSelector((state: RootState) => state.wallet); - const [walletData, setWalletData] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const walletId = localStorage.getItem('currentWalletId'); - - if (!walletId) { - setError('No wallet ID found'); - setLoading(false); - return; - } - - // Fetch approvers and wallet details - dispatch(fetchApproverListThunk({ accountId: walletId })); - dispatch(fetchMultisigAccountDetailsThunk({ accountId: walletId })); - - setLoading(false); - }, [dispatch]); - - // Update walletData when approvers or walletDataFromStore changes - useEffect(() => { - const updateWalletData = () => { - const walletFormDataString = localStorage.getItem('walletFormData'); - let walletFormData: WalletFormData | undefined; - - if (walletFormDataString) { - try { - walletFormData = JSON.parse(walletFormDataString); - } catch (parseError) { - console.warn('Failed to parse walletFormData from localStorage:', parseError); - } - } - - const updatedWalletData: WalletData = { - approver_number: approvers.length, - kind: walletDataFromStore?.kind || '', - threshold: walletDataFromStore?.threshold || 0, - approver: approvers.map(approver => approver.address), - walletFormData - }; - setWalletData(updatedWalletData); - }; - - updateWalletData(); - }, [approvers, walletDataFromStore]); - - return { walletData, loading, error }; -}; diff --git a/bin/coordinator-frontend/src/interactions/ApproveFundTransfer.tsx b/bin/coordinator-frontend/src/interactions/ApproveFundTransfer.tsx index 71857b6..d3b1348 100644 --- a/bin/coordinator-frontend/src/interactions/ApproveFundTransfer.tsx +++ b/bin/coordinator-frontend/src/interactions/ApproveFundTransfer.tsx @@ -1,78 +1,68 @@ "use client"; -import React, { useEffect, useState } from "react"; -import { useAppDispatch, useAppSelector } from "../store/hooks"; -import { fetchPendingTransactions } from "../services/transactionApi"; -import { TransferBox } from "../components/TransferBox"; +import React, { useMemo, useState } from "react"; +import { useMultisig } from "@/contexts/MultisigContext"; +import { toast } from "sonner"; +import { getEffectiveThreshold } from "@/lib/procedures"; export const ApproveFundTransfer = ({ onCancel, - threshold = 3, }: { onCancel?: () => void; - threshold?: number; }) => { - const dispatch = useAppDispatch(); - const { pendingTransactions, loading, error } = useAppSelector((state) => state.transaction); - const [selectedTxIds, setSelectedTxIds] = useState([]); - - useEffect(() => { - const walletId = localStorage.getItem("currentWalletId"); - if (walletId) { - dispatch(fetchPendingTransactions({ accountId: walletId })); - } - }, [dispatch]); + const { + proposals, + detectedConfig, + handleSignProposal, + handleExecuteProposal, + signingProposal, + executingProposal, + syncingState, + } = useMultisig(); + + const [selectedIds, setSelectedIds] = useState([]); + + const pendingProposals = useMemo(() => { + return proposals.filter(p => p.status.type === 'pending' || p.status.type === 'ready'); + }, [proposals]); + + const threshold = detectedConfig?.threshold ?? 0; const handleSelectAll = () => { - if (selectedTxIds.length === pendingTransactions.length) { - setSelectedTxIds([]); // Deselect all + if (selectedIds.length === pendingProposals.length) { + setSelectedIds([]); } else { - setSelectedTxIds(pendingTransactions.map(tx => tx.id)); + setSelectedIds(pendingProposals.map(p => p.id)); } }; - const handleTransactionSelect = (txId: string) => { - setSelectedTxIds(prev => - prev.includes(txId) - ? prev.filter(id => id !== txId) - : [...prev, txId] + const handleToggle = (id: string) => { + setSelectedIds(prev => + prev.includes(id) + ? prev.filter(x => x !== id) + : [...prev, id] ); }; - + const handleSignSelected = async () => { + for (const id of selectedIds) { + try { + await handleSignProposal(id); + } catch (err) { + toast.error(`Failed to sign ${id.slice(0, 8)}...`); + } + } + setSelectedIds([]); + toast.success("Signed selected proposals"); + }; - if (loading) { + if (syncingState) { return ( -
+
-

Loading pending transactions...

-
-
-
-
- ); - } - - if (error) { - return ( -
-
-
-
-

Error: {error}

- +

Loading proposals...

@@ -88,49 +78,95 @@ export const ApproveFundTransfer = ({ APPROVE QUEUED TRANSFERS
- {/* Full-width separator line */}
- pending your signature ({pendingTransactions.length}) + pending your signature ({pendingProposals.length})
- {pendingTransactions.length === 0 ? ( + {pendingProposals.length === 0 ? (
- No pending transactions to approve + No pending proposals to approve
) : ( <>
- {pendingTransactions.map((transaction, index) => ( - - ))} + {pendingProposals.map((proposal) => { + const propThreshold = getEffectiveThreshold( + proposal.metadata?.proposalType, + threshold, + detectedConfig?.procedureThresholds + ); + const sigCount = proposal.signatures?.length ?? 0; + const isReady = sigCount >= propThreshold; + const isSigning = signingProposal === proposal.id; + const isExecuting = executingProposal === proposal.id; + const isSelected = selectedIds.includes(proposal.id); + + return ( +
+ handleToggle(proposal.id)} + className="mr-3" + /> +
+
+ {proposal.metadata?.proposalType === 'p2id' ? 'SEND' : + proposal.metadata?.proposalType === 'consume_notes' ? 'RECEIVE' : + (proposal.metadata?.proposalType ?? 'UNKNOWN').toUpperCase().replace('_', ' ')} +
+
+ ID: {proposal.id.slice(0, 16)}... +
+
+
+ {sigCount}/{propThreshold} signed +
+ {isReady ? ( + + ) : ( + + )} +
+ ); + })}
- {selectedTxIds.length > 0 && ( + {selectedIds.length > 0 && (
)} @@ -141,10 +177,9 @@ export const ApproveFundTransfer = ({ security notice -
Please verify all transfer details carefully before approving. - once executed, transfers cannot be reversed. + Once executed, transfers cannot be reversed.
diff --git a/bin/coordinator-frontend/src/interactions/InitiateFundTransfer.tsx b/bin/coordinator-frontend/src/interactions/InitiateFundTransfer.tsx index 5e789df..22f3bbf 100644 --- a/bin/coordinator-frontend/src/interactions/InitiateFundTransfer.tsx +++ b/bin/coordinator-frontend/src/interactions/InitiateFundTransfer.tsx @@ -1,275 +1,129 @@ "use client"; -import { useDispatch } from "react-redux"; -import { useState, useEffect, useRef } from "react"; -import { AppDispatch } from "@/store"; -import { setCurrentTransactionId } from "@/store/slices/transactionSlice"; +import { useState, useMemo } from "react"; import media from "../../public/media"; import Image from "next/image"; -import { useMidenSdk } from "../hooks/useMidenSdk"; -import { useMidenClient } from "../contexts/MidenClientContext"; -import { useWalletData } from "../hooks/useWalletData"; -import { Address, NoteType } from "@demox-labs/miden-sdk"; -import { proposeTransactionWithTxBzThunk, fetchPendingTransactions } from "../services/transactionApi"; -import { InitiateFundTransferProps } from "@/types"; +import { useMultisig } from "@/contexts/MultisigContext"; +import { toast } from "sonner"; -const InitiateFundTransfer = ({ onCancel, fungibleAssets, isLoading: isLoadingAssets, onOpen }: InitiateFundTransferProps) => { - const dispatch = useDispatch(); - const { Miden } = useMidenSdk(); - const { handle, isInitialized } = useMidenClient(); - const { walletData, loading: walletLoading } = useWalletData(); +interface InitiateFundTransferProps { + onCancel?: () => void; +} + +const InitiateFundTransfer = ({ onCancel }: InitiateFundTransferProps) => { + const { + handleCreateSendProposal, + creatingProposal, + detectedConfig, + } = useMultisig(); - // Form state const [formData, setFormData] = useState({ - recipientAddress: "", + recipientId: "", amount: "", - currency: "", - priority: "", - memo: "", + faucetId: "", }); - const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); const [isTransactionInitiated, setIsTransactionInitiated] = useState(false); - const [selectedFaucetId, setSelectedFaucetId] = useState(""); - const [isDropdownOpen, setIsDropdownOpen] = useState(false); - const dropdownRef = useRef(null); - const hasRefreshedRef = useRef(false); - - // Refresh assets when modal opens (only once) - useEffect(() => { - if (onOpen && !hasRefreshedRef.current) { - hasRefreshedRef.current = true; - onOpen(); - } - }, [onOpen]); - useEffect(() => { - if (fungibleAssets.length > 0 && !selectedFaucetId) { - setSelectedFaucetId(fungibleAssets[0].faucetId); - } - }, [fungibleAssets, selectedFaucetId]); - - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { - setIsDropdownOpen(false); - } - }; + const vaultBalances = detectedConfig?.vaultBalances ?? []; + const threshold = detectedConfig?.threshold ?? 0; + const signerCount = detectedConfig?.signerCommitments?.length ?? 0; - if (isDropdownOpen) { - document.addEventListener('mousedown', handleClickOutside); - } - - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [isDropdownOpen]); + const selectedBalance = useMemo(() => { + if (!formData.faucetId) return 0; + const b = vaultBalances.find(v => v.faucetId === formData.faucetId); + return b ? Number(b.amount) / 1000000 : 0; + }, [formData.faucetId, vaultBalances]); const handleInputChange = (field: string, value: string) => { - setFormData((prev) => ({ - ...prev, - [field]: value, - })); + setFormData((prev) => ({ ...prev, [field]: value })); }; - const handleCancel = () => { - // Clear any previous transaction ID when canceling - dispatch(setCurrentTransactionId(null)); - localStorage.removeItem("tx_id"); onCancel?.(); }; - const testInitiateFundTransfer = async () => { - try { - const currentWalletId = localStorage.getItem("currentWalletId"); - if (!currentWalletId) { - throw new Error("No wallet ID found in localStorage"); - } - - const senderAddress = Address.fromBech32(currentWalletId); - const senderAccountId = senderAddress.accountId(); - - const targetBech32 = formData.recipientAddress; - if (!targetBech32) { - throw new Error("No recipient address provided"); - } - - const targetAddress = Address.fromBech32(targetBech32); - const targetAccountId = targetAddress.accountId(); - - if (!selectedFaucetId) { - throw new Error("No faucet selected"); - } - const faucetAddress = Address.fromBech32(selectedFaucetId); - const faucetAccountId = faucetAddress.accountId(); - - const noteType = NoteType.Public; - - const amount = BigInt(Number(formData.amount) * 1000000 || "0"); - - if (!Miden) { - throw new Error("Miden SDK not loaded"); - } - - if (!Miden || !handle || !isInitialized) { - throw new Error("Miden client not initialized"); - } - - const webClient = handle.getWebClient(); - if (!webClient) { - throw new Error("WebClient not available"); - } - - // Create transaction request - const transactionRequest = webClient.newSendTransactionRequest( - senderAccountId, - targetAccountId, - faucetAccountId, - noteType, - amount - ); - - // Serialize and convert to hex - const serializedRequest = transactionRequest.serialize(); - const serializedRequestBase64 = serializedRequest.toBase64(); - - // Propose the transaction - const result = await dispatch(proposeTransactionWithTxBzThunk({ - accountId: currentWalletId, - txBz: serializedRequestBase64 - })).unwrap(); + const handleSubmit = async () => { + setError(null); - // Refresh pending transactions - await dispatch(fetchPendingTransactions({ accountId: currentWalletId })); - - // Close the modal - onCancel?.(); + if (!formData.recipientId.trim()) { + setError("Recipient account ID is required"); + return; + } + if (!formData.faucetId.trim()) { + setError("Please select a token"); + return; + } + if (!formData.amount || Number(formData.amount) <= 0) { + setError("Please enter a valid amount"); + return; + } - } catch (error) { - console.error("Error in testInitiateFundTransfer:", error); + try { + const amount = BigInt(Math.round(Number(formData.amount) * 1000000)); + await handleCreateSendProposal(formData.recipientId.trim(), formData.faucetId.trim(), amount); + setIsTransactionInitiated(true); + toast.success("Send proposal created!"); + setTimeout(() => onCancel?.(), 1500); + } catch (err) { + setError(err instanceof Error ? err.message : "Failed to create send proposal"); } }; return ( -
-
+
+
SEND FUNDS
-
- {/* Error Display */} {error && (
Error: {error}
)} -
+
- Recipient Address + Recipient Account ID
- handleInputChange("recipientAddress", e.target.value) - } - placeholder="Enter recipient address" + value={formData.recipientId} + onChange={(e) => handleInputChange("recipientId", e.target.value)} + placeholder="0x..." className="bg-[#FFFFFF] w-full lg:h-[40px] md:h-[40px] sm:h-[36px] h-[32px] border-[1.09px] border-[rgba(217,217,217,1)] px-3 pr-10 font-dmmono font-[500] text-[12px] focus:outline-none focus:ring-2 focus:ring-[#FF5500]/60" />
- warning + warning
- {/* amount and currency section starts here */}
- {/* currency section starts here */} -
-
-
- Token -
-
- {/* Custom Dropdown Button */} - - - {/* Dropdown Menu */} - {isDropdownOpen && ( -
- {isLoadingAssets ? ( -
-
-
- Loading tokens... -
-
- ) : fungibleAssets.length === 0 ? ( -
- No tokens available -
- ) : ( - fungibleAssets.map((asset, index) => ( - - )) - )} -
- )} -
+
+
+ Token (Faucet ID)
+
- BALANCE: {fungibleAssets.find(asset => asset.faucetId === selectedFaucetId)?.balance / 1000000 || "0"} + BALANCE: {selectedBalance.toFixed(2)}
- {/* amount section starts here */} -
+ +
Amount
@@ -285,7 +139,6 @@ const InitiateFundTransfer = ({ onCancel, fungibleAssets, isLoading: isLoadingAs
- {/* button section starts here */}
- {/* button section ends here */}
+ {isTransactionInitiated ? ( - <> -
-
- TRANSFER REQUEST INITIATED -
-
- Multi-signature transfer has been queued for approval -
+
+
+ TRANSFER REQUEST INITIATED +
+
+ Multi-signature transfer has been queued for approval
- +
) : ( - <> -
-
- Multi-signature required -
-
- This transfer will require {walletData?.threshold || walletData?.walletFormData?.signatureThreshold || "-"} of {walletData?.approver_number || walletData?.walletFormData?.totalSigners || "-"} approvals before execution. -
+
+
+ Multi-signature required +
+
+ This transfer will require {threshold || "-"} of {signerCount || "-"} approvals before execution.
- +
)} - {/* Loading Overlay */} - {isSubmitting && ( + {creatingProposal && (
- {/* Loading Spinner */}
- - {/* Main Text */} -
- TRANSACTION INITIATING +
+ CREATING PROPOSAL
- - {/* Subtitle */} -
- This may take a few seconds depending on server load. +
+ This may take a few seconds.
diff --git a/bin/coordinator-frontend/src/interactions/PendingTransactionDetails.tsx b/bin/coordinator-frontend/src/interactions/PendingTransactionDetails.tsx deleted file mode 100644 index 8115d6d..0000000 --- a/bin/coordinator-frontend/src/interactions/PendingTransactionDetails.tsx +++ /dev/null @@ -1,83 +0,0 @@ -"use client" -import React from "react"; - -const PendingTransactionDetails = ({ onClose }: { onClose: () => void }) => { - return ( -
- -
-
pending transaction details
-
- -
- transaction overview - -
- action type - send -
- -
-
- amount - -150 miden -
-
-
- recipient - mts123..rfgr -
-
- -
- -
- MULTI-SIGNATURE STATUS - -
- SIGNATURES REQUIRED - 3 OF 3 -
- -
-
- CURRENT - 3 -
-
-
- REMAINING - 0 -
-
- -
- -
- NETWORK DETAILS - -
- TRANSACTION ID - TX_2_PENDING -
- -
-
- NETWORK - MIDEN TESTNET -
-
-
- GAS FEE - 0.001 MIDEN -
-
- -
CLOSE
-
- -
- ); -}; - -export default PendingTransactionDetails; \ No newline at end of file diff --git a/bin/coordinator-frontend/src/interactions/ReceiveFundTransfer.tsx b/bin/coordinator-frontend/src/interactions/ReceiveFundTransfer.tsx index 485cc14..62441a1 100644 --- a/bin/coordinator-frontend/src/interactions/ReceiveFundTransfer.tsx +++ b/bin/coordinator-frontend/src/interactions/ReceiveFundTransfer.tsx @@ -1,99 +1,24 @@ "use client"; -import { useState, useEffect } from "react"; -import { useMidenClient } from "@/contexts/MidenClientContext"; -import { useDispatch, useSelector } from "react-redux"; -import { AppDispatch } from "@/store"; -import { NoteFile } from "@demox-labs/miden-sdk"; -import { proposeTransactionWithTxBzThunk, fetchPendingTransactions, fetchConfirmedTransactions, getConsumableNotesThunk } from "../services/transactionApi"; +import { useState, useMemo } from "react"; +import { useMultisig } from "@/contexts/MultisigContext"; +import { toast } from "sonner"; -const getReceiveTransactionAmount = async (noteId: string, noteIdFileBytes: string, webClient: any): Promise => { - try { - if (!noteId || typeof noteId !== "string" || noteId.trim() === "") { - return 0; - } - - if (!webClient) { - console.error("WebClient not available"); - return 0; - } - - let inputNoteRecord = await webClient.getInputNote(noteId); - if (!inputNoteRecord) { - if (noteIdFileBytes) { - const noteBytes = Uint8Array.fromBase64(noteIdFileBytes); - const noteFile = NoteFile.deserialize(noteBytes); - await webClient.importNoteFile(noteFile); - inputNoteRecord = await webClient.getInputNote(noteId); - } else { - console.error("No note file bytes to import"); - return 0; - } - } - - if (inputNoteRecord) { - const details = inputNoteRecord.details(); - const assets = details.assets(); - const fungibleAssets = assets.fungibleAssets(); - - if (fungibleAssets.length > 0) { - const amount = fungibleAssets[0].amount(); - return Number(amount) / 1000000; - } - } +const ReceiveFundTransfer = ({ onCancel }: { onCancel?: () => void }) => { + const { + consumableNotes, + handleCreateConsumeNotesProposal, + creatingProposal, + } = useMultisig(); - return 0; - } catch (error) { - console.error("Error extracting amount from note:", error); - return 0; - } -}; - -const ReceiveFundTransfer = ({ onCancel, onAssetsUpdated }: { onCancel?: () => void; onAssetsUpdated?: () => Promise }) => { - const { handle, isInitialized } = useMidenClient(); - const dispatch = useDispatch(); - const { consumableNotes, loading } = useSelector((state: any) => state.transaction); const [selectedNoteIds, setSelectedNoteIds] = useState([]); - const [isConsuming, setIsConsuming] = useState(false); - const [noteAmounts, setNoteAmounts] = useState<{ [noteId: string]: number }>({}); - const [amountsLoading, setAmountsLoading] = useState(false); - - const webClient = handle.getWebClient(); - - const noteIds = consumableNotes?.note_ids || []; - - useEffect(() => { - dispatch(getConsumableNotesThunk()); - }, [dispatch]); - - useEffect(() => { - const calculateAmounts = async () => { - if (!webClient || noteIds.length === 0) { - return; - } - - setAmountsLoading(true); - const amounts: { [noteId: string]: number } = {}; - for (const note of noteIds) { - const noteId = note.note_id || ''; - const noteIdFileBytes = note.note_id_file_bytes || ''; - - const amount = await getReceiveTransactionAmount(noteId, noteIdFileBytes, webClient); - amounts[noteId] = amount; - } - - setNoteAmounts(amounts); - setAmountsLoading(false); - }; - - calculateAmounts(); - }, [noteIds, webClient]); + const notes = useMemo(() => consumableNotes ?? [], [consumableNotes]); const handleSelectAll = () => { - if (selectedNoteIds.length === noteIds.length) { + if (selectedNoteIds.length === notes.length) { setSelectedNoteIds([]); } else { - setSelectedNoteIds(noteIds.map((note: any) => note.note_id)); + setSelectedNoteIds(notes.map(n => n.id)); } }; @@ -107,61 +32,16 @@ const ReceiveFundTransfer = ({ onCancel, onAssetsUpdated }: { onCancel?: () => v const handleClaimSelected = async () => { if (selectedNoteIds.length === 0) { - alert("Please select at least one note"); - return; - } - - if (!handle || !isInitialized) { - alert("Miden client not initialized"); - return; - } - - if (!webClient) { - alert("WebClient not available"); + toast.error("Please select at least one note"); return; } - setIsConsuming(true); try { - const transactionRequest = webClient.newConsumeTransactionRequest(selectedNoteIds); - const serializedRequest = transactionRequest.serialize(); - - const tx_bz = serializedRequest.toBase64(); - console.info('[ReceiveFundTransfer] Proposing consume transaction', { - selectedCount: selectedNoteIds.length, - }); - - const result = await dispatch(proposeTransactionWithTxBzThunk({ - accountId: localStorage.getItem("currentWalletId") || "", - txBz: tx_bz - })).unwrap(); - - try { - const currentWalletId = localStorage.getItem("currentWalletId"); - if (currentWalletId) { - await Promise.all([ - dispatch(fetchPendingTransactions({ accountId: currentWalletId })), - dispatch(fetchConfirmedTransactions({ accountId: currentWalletId })) - ]); - } - } catch (refreshError) { - console.warn("Failed to refresh transactions:", refreshError); - } - - if (onAssetsUpdated) { - try { - await onAssetsUpdated(); - } catch (assetsError) { - console.warn("Failed to refresh assets:", assetsError); - } - } - + await handleCreateConsumeNotesProposal(selectedNoteIds); + toast.success("Consume notes proposal created!"); onCancel?.(); } catch (error) { - console.error("Error consuming notes:", error); - alert(`Error consuming notes: ${error instanceof Error ? error.message : 'Unknown error'}`); - } finally { - setIsConsuming(false); + toast.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`); } }; @@ -171,14 +51,10 @@ const ReceiveFundTransfer = ({ onCancel, onAssetsUpdated }: { onCancel?: () => v
- {loading || amountsLoading ? ( -
Loading notes...
- ) : noteIds.length > 0 ? ( - noteIds.map((note: any, index: number) => { - const noteId = note.note_id || ''; - const noteIdFileBytes = note.note_id_file_bytes || ''; - const isSelected = selectedNoteIds.includes(noteId); - const amount = noteAmounts[noteId] || 0; + {notes.length > 0 ? ( + notes.map((note, index) => { + const isSelected = selectedNoteIds.includes(note.id); + const totalAmount = note.assets.reduce((sum, a) => sum + Number(a.amount), 0) / 1000000; return (
@@ -186,26 +62,27 @@ const ReceiveFundTransfer = ({ onCancel, onAssetsUpdated }: { onCancel?: () => v handleToggleNote(noteId)} + onChange={() => handleToggleNote(note.id)} className="h-[12px] w-[12px] border-[0.5px]" /> -
Receive {amount.toFixed(2)} MIDEN
+
Receive {totalAmount.toFixed(2)} MIDEN
- - + + {note.id.slice(0, 12)}... +
); }) ) : ( -
No consumable notes available
+
No consumable notes available
)}
Security notice
-
Please verify the sender address and amount before claiming. Ensure this transfer is expected and legitimate. once executed, transfers cannot be reversed.
+
Please verify the sender address and amount before claiming. Ensure this transfer is expected and legitimate. Once executed, transfers cannot be reversed.
@@ -223,10 +100,10 @@ const ReceiveFundTransfer = ({ onCancel, onAssetsUpdated }: { onCancel?: () => v
diff --git a/bin/coordinator-frontend/src/interactions/signTransaction.tsx b/bin/coordinator-frontend/src/interactions/signTransaction.tsx index 3e0cb2a..ac82d98 100644 --- a/bin/coordinator-frontend/src/interactions/signTransaction.tsx +++ b/bin/coordinator-frontend/src/interactions/signTransaction.tsx @@ -1,107 +1,109 @@ -"use client" -import { addSignatureThunk, fetchPendingTransactions, fetchConfirmedTransactions } from "@/services"; -import { useState } from "react"; -import { useAppDispatch, useAppSelector } from "@/store/hooks"; +"use client"; +import React, { useMemo, useState } from "react"; +import { useMultisig } from "@/contexts/MultisigContext"; +import { toast } from "sonner"; +import { getEffectiveThreshold } from "@/lib/procedures"; -const SignTransaction = ({ transactionId, onCancel }: { transactionId: string, onCancel?: () => void }) => { - const dispatch = useAppDispatch(); - - // Add missing state variables - const [formData, setFormData] = useState({ - approverAddress: '', - signature: '' - }); - const [error, setError] = useState(null); - const [isSubmitting, setIsSubmitting] = useState(false); - const [success, setSuccess] = useState(null); +const SignTransaction = ({ + transactionId, + onCancel, +}: { + transactionId: string; + onCancel?: () => void; +}) => { + const { + proposals, + detectedConfig, + handleSignProposal, + handleExecuteProposal, + signingProposal, + executingProposal, + } = useMultisig(); - const handleInputChange = (field: string, value: string) => { - setFormData(prev => ({ - ...prev, - [field]: value - })); - }; + const threshold = detectedConfig?.threshold ?? 0; - const handleSubmit = async () => { - if (!formData.approverAddress.trim()) { - setError('Approver address is required'); - return; - } - - if (!formData.signature.trim()) { - setError('Signature is required'); - return; - } - - if (!transactionId) { - setError('No transaction ID found. Please initiate a transaction first.'); - return; + const proposal = useMemo(() => { + return proposals.find((p) => p.id === transactionId); + }, [proposals, transactionId]); + + const propThreshold = proposal + ? getEffectiveThreshold( + proposal.metadata?.proposalType, + threshold, + detectedConfig?.procedureThresholds + ) + : threshold; + const sigCount = proposal?.signatures?.length ?? 0; + const isReady = sigCount >= propThreshold; + const isSigning = signingProposal === transactionId; + const isExecuting = executingProposal === transactionId; + + const handleSign = async () => { + try { + await handleSignProposal(transactionId); + toast.success("Proposal signed successfully"); + } catch (err) { + toast.error( + err instanceof Error ? err.message : "Failed to sign proposal" + ); } - - setIsSubmitting(true); - setError(null); - setSuccess(null); - + }; + + const handleExecute = async () => { try { - // Prepare the signature data according to the API payload structure - const signatureData = { - approver_address: formData.approverAddress, - signature: formData.signature - }; - - - // Call the addSignatureThunk - const result = await dispatch(addSignatureThunk({ - txId: transactionId, - signatureData - })).unwrap(); - - - setSuccess('Signature added successfully!'); - - try { - const walletId = localStorage.getItem('currentWalletId'); - if (walletId) { - await Promise.all([ - dispatch(fetchPendingTransactions({ accountId: walletId })), - dispatch(fetchConfirmedTransactions({ accountId: walletId })) - ]); - } - } catch (refreshError) { - console.warn('Failed to refresh transaction data:', refreshError); - } - - // Add a small delay to show success state - await new Promise(resolve => setTimeout(resolve, 1500)); - - // Close modal on success - if (onCancel) { - onCancel(); - } + await handleExecuteProposal(transactionId); + toast.success("Proposal executed successfully"); + onCancel?.(); } catch (err) { - console.error('Error signing transaction:', err); - setError(err instanceof Error ? err.message : 'Failed to sign transaction'); - } finally { - setIsSubmitting(false); + toast.error( + err instanceof Error ? err.message : "Failed to execute proposal" + ); } }; const handleCancel = () => { - if (onCancel) { - onCancel(); - } + onCancel?.(); }; + if (!proposal) { + return ( +
+
+ Approve Transaction +
+
+
+
+ Proposal not found +
+ +
+
+ ); + } + return ( - <> - -
Approve Transaction
@@ -110,83 +112,104 @@ const SignTransaction = ({ transactionId, onCancel }: { transactionId: string, o className="w-full" >
- {/* Transaction ID Display */} + {/* Proposal ID Display */}
- Transaction ID: {transactionId } + Proposal ID: {transactionId}
- - {/* Error Display */} - {error && ( -
- Error: {error} -
- )} - - {/* Success Display */} - {success && ( -
- {success} + + {/* Proposal Details */} +
+
+ + Type + + + {proposal.metadata?.proposalType === "p2id" + ? "SEND" + : proposal.metadata?.proposalType === "consume_notes" + ? "RECEIVE" + : proposal.metadata?.proposalType.toUpperCase().replace("_", " ")} +
- )} - -
-
- Approver Address +
+
+ + Signatures + + + {sigCount}/{propThreshold} +
- handleInputChange('approverAddress', e.target.value)} - placeholder="Enter approver address" - className="bg-[#FFFFFF] w-full lg:h-[40px] md:h-[40px] sm:h-[36px] h-[32px] border-[1.09px] border-[rgba(217,217,217,1)] px-3 font-dmmono font-[500] text-[12px] focus:outline-none focus:ring-2 focus:ring-[#FF5500]/60" - /> -
- -
-
- Signature +
+
+ + Status + + + {isReady ? "READY TO EXECUTE" : "PENDING SIGNATURES"} +
- handleInputChange('signature', e.target.value)} - placeholder="Enter signature (0x...)" - className="bg-[#FFFFFF] w-full lg:h-[40px] md:h-[40px] sm:h-[36px] h-[32px] border-[1.09px] border-[rgba(217,217,217,1)] px-3 font-dmmono font-[500] text-[12px] focus:outline-none focus:ring-2 focus:ring-[#FF5500]/60" - />
- -
- {/* button section starts here */} -
+ + {/* Action buttons */} +
+ + {isReady ? ( - -
- {/* button section ends here */} + )}
- ); }; diff --git a/bin/coordinator-frontend/src/lib/errors.ts b/bin/coordinator-frontend/src/lib/errors.ts new file mode 100644 index 0000000..0e5a4b0 --- /dev/null +++ b/bin/coordinator-frontend/src/lib/errors.ts @@ -0,0 +1,29 @@ +export function formatError(err: unknown, prefix?: string): string { + const message = + err instanceof Error + ? err.message + : typeof err === 'string' + ? err + : 'Unknown error'; + return prefix ? `${prefix}: ${message}` : message; +} + +export function classifyWalletError(err: unknown): string { + const msg = err instanceof Error ? err.message : String(err); + const name = err instanceof Error ? err.name : ''; + const lower = (msg + ' ' + name).toLowerCase(); + + if (lower.includes('user cancelled') || lower.includes('user rejected') || lower.includes('user denied')) { + return 'Signing was cancelled'; + } + if (lower.includes('walletnotready') || lower.includes('not detected') || lower.includes('not found') || lower.includes('not installed')) { + return 'Wallet extension not detected. Please install the Miden Wallet browser extension.'; + } + if (lower.includes('not connected') || lower.includes('no wallet')) { + return 'Wallet is not connected'; + } + if (lower.includes('invalid signature') || lower.includes('signature format')) { + return 'Invalid signature format'; + } + return msg || name || 'Unknown wallet error'; +} diff --git a/bin/coordinator-frontend/src/lib/helpers.ts b/bin/coordinator-frontend/src/lib/helpers.ts new file mode 100644 index 0000000..a0133b7 --- /dev/null +++ b/bin/coordinator-frontend/src/lib/helpers.ts @@ -0,0 +1,37 @@ +export function normalizeCommitment(hex: string): string { + const trimmed = hex.trim(); + if (!trimmed) throw new Error('Commitment is required'); + const withoutPrefix = + trimmed.startsWith('0x') || trimmed.startsWith('0X') ? trimmed.slice(2) : trimmed; + if (!/^[0-9a-fA-F]{64}$/.test(withoutPrefix)) { + throw new Error('Commitment must be a 64-character hex string'); + } + return `0x${withoutPrefix.toLowerCase()}`; +} + +export function copyToClipboard(text: string, onSuccess?: () => void): void { + navigator.clipboard.writeText(text).then(() => { + onSuccess?.(); + }); +} + +export async function clearIndexedDB(): Promise { + const databases = await indexedDB.databases(); + const deletePromises = databases + .filter((db) => db.name) + .map( + (db) => + new Promise((resolve, reject) => { + const request = indexedDB.deleteDatabase(db.name!); + request.onsuccess = () => resolve(); + request.onerror = () => reject(request.error); + request.onblocked = () => resolve(); + }) + ); + await Promise.all(deletePromises); +} + +export function truncateHex(hex: string, start = 16, end = 8): string { + if (hex.length <= start + end) return hex; + return `${hex.slice(0, start)}...${hex.slice(-end)}`; +} diff --git a/bin/coordinator-frontend/src/lib/initClient.ts b/bin/coordinator-frontend/src/lib/initClient.ts new file mode 100644 index 0000000..ebc1483 --- /dev/null +++ b/bin/coordinator-frontend/src/lib/initClient.ts @@ -0,0 +1,108 @@ +import { WebClient, AuthSecretKey } from '@miden-sdk/miden-sdk'; +import { MIDEN_DB_NAME, MIDEN_RPC_URL } from '@/config/psm'; +import { normalizeCommitment } from '@/lib/helpers'; +import type { SignerInfo } from '@/types/psm'; + +const SIGNER_DB_NAME = 'MultisigSignerKeys'; +const SIGNER_STORE_NAME = 'keys'; + +export async function clearMidenDatabase(dbName = MIDEN_DB_NAME): Promise { + return new Promise((resolve, reject) => { + const request = indexedDB.deleteDatabase(dbName); + request.onsuccess = () => resolve(); + request.onerror = () => reject(request.error); + request.onblocked = () => resolve(); + }); +} + +function openSignerDB(): Promise { + return new Promise((resolve, reject) => { + const request = indexedDB.open(SIGNER_DB_NAME, 1); + request.onupgradeneeded = () => { + const db = request.result; + if (!db.objectStoreNames.contains(SIGNER_STORE_NAME)) { + db.createObjectStore(SIGNER_STORE_NAME); + } + }; + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); +} + +export async function saveSignerKeys(signer: SignerInfo): Promise { + const db = await openSignerDB(); + const tx = db.transaction(SIGNER_STORE_NAME, 'readwrite'); + const store = tx.objectStore(SIGNER_STORE_NAME); + store.put(signer.falcon.secretKey.serialize(), 'falcon'); + store.put(signer.ecdsa.secretKey.serialize(), 'ecdsa'); + return new Promise((resolve, reject) => { + tx.oncomplete = () => { db.close(); resolve(); }; + tx.onerror = () => { db.close(); reject(tx.error); }; + }); +} + +export async function loadSignerKeys(): Promise { + try { + const db = await openSignerDB(); + const tx = db.transaction(SIGNER_STORE_NAME, 'readonly'); + const store = tx.objectStore(SIGNER_STORE_NAME); + + const [falconBytes, ecdsaBytes] = await Promise.all([ + new Promise((resolve, reject) => { + const req = store.get('falcon'); + req.onsuccess = () => resolve(req.result as Uint8Array | undefined); + req.onerror = () => reject(req.error); + }), + new Promise((resolve, reject) => { + const req = store.get('ecdsa'); + req.onsuccess = () => resolve(req.result as Uint8Array | undefined); + req.onerror = () => reject(req.error); + }), + ]); + + db.close(); + + if (!falconBytes || !ecdsaBytes) return null; + + const falconSecretKey = AuthSecretKey.deserialize(falconBytes); + const ecdsaSecretKey = AuthSecretKey.deserialize(ecdsaBytes); + const falconCommitment = normalizeCommitment(falconSecretKey.publicKey().toCommitment().toHex()); + const ecdsaCommitment = normalizeCommitment(ecdsaSecretKey.publicKey().toCommitment().toHex()); + + return { + falcon: { commitment: falconCommitment, secretKey: falconSecretKey }, + ecdsa: { commitment: ecdsaCommitment, secretKey: ecdsaSecretKey }, + activeScheme: 'falcon', + }; + } catch { + return null; + } +} + +export async function clearSignerKeys(): Promise { + return new Promise((resolve) => { + const request = indexedDB.deleteDatabase(SIGNER_DB_NAME); + request.onsuccess = () => resolve(); + request.onerror = () => resolve(); + request.onblocked = () => resolve(); + }); +} + +export async function createWebClient(rpcUrl = MIDEN_RPC_URL): Promise { + const client = await WebClient.createClient(rpcUrl); + await client.syncState(); + return client; +} + +export function initializeSigner(): SignerInfo { + const falconSecretKey = AuthSecretKey.rpoFalconWithRNG(undefined); + const ecdsaSecretKey = AuthSecretKey.ecdsaWithRNG(undefined); + const falconCommitment = normalizeCommitment(falconSecretKey.publicKey().toCommitment().toHex()); + const ecdsaCommitment = normalizeCommitment(ecdsaSecretKey.publicKey().toCommitment().toHex()); + + return { + falcon: { commitment: falconCommitment, secretKey: falconSecretKey }, + ecdsa: { commitment: ecdsaCommitment, secretKey: ecdsaSecretKey }, + activeScheme: 'falcon', + }; +} diff --git a/bin/coordinator-frontend/src/lib/multisigApi.ts b/bin/coordinator-frontend/src/lib/multisigApi.ts new file mode 100644 index 0000000..6eb08c7 --- /dev/null +++ b/bin/coordinator-frontend/src/lib/multisigApi.ts @@ -0,0 +1,98 @@ +import { + type Multisig, + type MultisigClient, + type MultisigConfig, + type ProcedureThreshold, + type ParaSigningContext, + type WalletSigningContext, + MultisigClient as MultisigClientClass, + FalconSigner, + EcdsaSigner, + ParaSigner, + MidenWalletSigner, + type SignatureScheme, +} from '@openzeppelin/miden-multisig-client'; +import type { Signer } from '@openzeppelin/psm-client'; +import type { WebClient } from '@miden-sdk/miden-sdk'; +import type { SignerInfo } from '@/types/psm'; +import type { WalletSource } from '@/wallets/types'; +import { normalizeCommitment } from '@/lib/helpers'; + +export interface ExternalSignerParams { + walletSource: WalletSource; + paraContext?: { para: ParaSigningContext; walletId: string; commitment: string; publicKey: string }; + midenWalletContext?: { wallet: WalletSigningContext; commitment: string; scheme: SignatureScheme }; +} + +export function createSigner( + signerInfo: SignerInfo, + signatureScheme: SignatureScheme, + external?: ExternalSignerParams, +): Signer { + if (external?.walletSource === 'para' && external.paraContext) { + const ctx = external.paraContext; + return new ParaSigner(ctx.para, ctx.walletId, ctx.commitment, ctx.publicKey); + } + + if (external?.walletSource === 'miden-wallet' && external.midenWalletContext) { + const ctx = external.midenWalletContext; + if (ctx.scheme === 'ecdsa') { + const localSigner = new EcdsaSigner(signerInfo.ecdsa.secretKey); + return new MidenWalletSigner(ctx.wallet, ctx.commitment, ctx.scheme, localSigner); + } + return new MidenWalletSigner(ctx.wallet, ctx.commitment, ctx.scheme); + } + + const activeSigner = signatureScheme === 'ecdsa' ? signerInfo.ecdsa : signerInfo.falcon; + return signatureScheme === 'ecdsa' + ? new EcdsaSigner(activeSigner.secretKey) + : new FalconSigner(activeSigner.secretKey); +} + +export async function initMultisigClient( + webClient: WebClient, + psmEndpoint: string, + scheme?: SignatureScheme, +): Promise<{ client: MultisigClient; psmCommitment: string; psmPubkey?: string }> { + const client = new MultisigClientClass(webClient, { psmEndpoint }); + const { psmCommitment, psmPublicKey } = await client.initialize(scheme); + return { client, psmCommitment, psmPubkey: psmPublicKey }; +} + +export async function createMultisigAccount( + multisigClient: MultisigClient, + signerCommitment: string, + otherCommitments: string[], + threshold: number, + psmCommitment: string, + signer: Signer, + psmPublicKey?: string, + procedureThresholds?: ProcedureThreshold[], + signatureScheme: SignatureScheme = 'falcon', +): Promise { + const signerCommitments = [signerCommitment, ...otherCommitments].map(normalizeCommitment); + const config: MultisigConfig = { + threshold, + signerCommitments, + psmCommitment, + psmPublicKey, + psmEnabled: true, + procedureThresholds, + storageMode: 'private', + signatureScheme, + }; + return multisigClient.create(config, signer); +} + +export async function loadMultisigAccount( + multisigClient: MultisigClient, + accountId: string, + signer: Signer, + psmPublicKey?: string, +): Promise { + const multisig = await multisigClient.load(accountId, signer); + if (psmPublicKey) { + multisig.setPsmPublicKey(psmPublicKey); + } + return multisig; +} diff --git a/bin/coordinator-frontend/src/lib/procedures.ts b/bin/coordinator-frontend/src/lib/procedures.ts new file mode 100644 index 0000000..3e82974 --- /dev/null +++ b/bin/coordinator-frontend/src/lib/procedures.ts @@ -0,0 +1,48 @@ +import type { ProcedureName, ProposalType } from '@openzeppelin/miden-multisig-client'; + +export interface ProcedureInfo { + name: ProcedureName; + label: string; + description: string; +} + +export const USER_PROCEDURES: ProcedureInfo[] = [ + { name: 'receive_asset', label: 'Receive Assets', description: 'Accept incoming assets' }, + { name: 'send_asset', label: 'Send Assets', description: 'Send assets to other accounts' }, + { name: 'update_signers', label: 'Update Signers', description: 'Add/remove signers or change threshold' }, + { name: 'update_psm', label: 'Update PSM', description: 'Change PSM server configuration' }, +]; + +export function getProposalProcedure(proposalType: ProposalType): ProcedureName | null { + switch (proposalType) { + case 'p2id': + return 'send_asset'; + case 'consume_notes': + return 'receive_asset'; + case 'add_signer': + case 'remove_signer': + case 'change_threshold': + return 'update_signers'; + case 'switch_psm': + return 'update_psm'; + default: + return null; + } +} + +export function getEffectiveThreshold( + proposalType: ProposalType, + defaultThreshold: number, + procedureThresholds?: Map, +): number { + if (!procedureThresholds || procedureThresholds.size === 0) { + return defaultThreshold; + } + + const procedure = getProposalProcedure(proposalType); + if (!procedure) { + return defaultThreshold; + } + + return procedureThresholds.get(procedure) ?? defaultThreshold; +} diff --git a/bin/coordinator-frontend/src/services/index.ts b/bin/coordinator-frontend/src/services/index.ts deleted file mode 100644 index d5768dc..0000000 --- a/bin/coordinator-frontend/src/services/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Export all API functions from a single location -export * from './walletApi'; -export * from './transactionApi'; -export * from './signatureApi'; \ No newline at end of file diff --git a/bin/coordinator-frontend/src/services/signatureApi.ts b/bin/coordinator-frontend/src/services/signatureApi.ts deleted file mode 100644 index b7c2276..0000000 --- a/bin/coordinator-frontend/src/services/signatureApi.ts +++ /dev/null @@ -1,73 +0,0 @@ -// API service for signature operations -import { createAsyncThunk } from '@reduxjs/toolkit'; -import { setLoading, setError } from '../store/slices/signatureSlice'; -import { - AddSignatureRequest -} from '../types'; -import { COORDINATOR_API_BASE_URL } from '../config/api'; - -export const addSignature = async (txId: string, signatureData: AddSignatureRequest): Promise => { - try { - const payload = signatureData; - - const response = await fetch(`${COORDINATOR_API_BASE_URL}/api/v1/signature/add`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error('Signature API error response:', errorText); - throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); - } - - const responseText = await response.text(); - - if (responseText.trim() === '') { - return true; - } - - if (/^\d+$/.test(responseText.trim())) { - - return true; - } - - try { - const responseData = JSON.parse(responseText); - return true; - } catch (parseError) { - return true; - } - } catch (error) { - console.error('Error adding signature:', error); - throw error; - } -}; - -export const addSignatureThunk = createAsyncThunk( - 'signature/addSignature', - async ({ txId, signatureData }: { txId: string; signatureData: AddSignatureRequest }, { dispatch }) => { - try { - dispatch(setLoading(true)); - dispatch(setError(null)); - - - const result = await addSignature(txId, signatureData); - - - await new Promise(resolve => setTimeout(resolve, 1000)); - - return result; - } catch (error) { - console.error('Error in addSignatureThunk:', error); - const errorMessage = error instanceof Error ? error.message : 'Failed to add signature'; - dispatch(setError(errorMessage)); - throw error; - } finally { - dispatch(setLoading(false)); - } - } -); \ No newline at end of file diff --git a/bin/coordinator-frontend/src/services/transactionApi.ts b/bin/coordinator-frontend/src/services/transactionApi.ts deleted file mode 100644 index 1dd0d5e..0000000 --- a/bin/coordinator-frontend/src/services/transactionApi.ts +++ /dev/null @@ -1,386 +0,0 @@ -// API service for transaction operations -import { createAsyncThunk } from '@reduxjs/toolkit'; -import { setPendingTransactions, setAllTransactions, setLoading, setError } from '../store/slices/transactionSlice'; -import { setWalletStats as setWalletStatsAction, setLoading as setWalletStatsLoading, setError as setWalletStatsError } from '../store/slices/walletStatsSlice'; -import { setApprovers, setApproversLoading, setApproversError, setWalletData, setWalletDataLoading, setWalletDataError } from '../store/slices/walletSlice'; -import { - GetAccountTransactionsResponse, - CreateTransactionRequest, - GetTransactionStatsResponse, - GetApproverListResponse, - GetMultisigAccountDetailsResponse, - ConsumableNote -} from '../types'; -import { COORDINATOR_API_BASE_URL } from '../config/api'; - -// Get Account Transactions -export const getAccountTransactions = async (accountId: string, status?: string): Promise => { - try { - const requestBody = { - multisig_account_address: accountId, - tx_status_filter: status - }; - - const response = await fetch(`${COORDINATOR_API_BASE_URL}/api/v1/multisig-tx/list`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - if (response.status === 404) { - localStorage.removeItem('currentWalletId'); - document.cookie = 'currentWalletId=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'; - } - throw new Error(`HTTP error! status: ${response.status}`); - } - - return await response.json(); - } catch (error) { - console.error('Error getting account transactions:', error); - throw error; - } -}; - -// Thunk for fetching pending transactions -export const fetchPendingTransactions = createAsyncThunk( - 'transaction/fetchPendingTransactions', - async ({ accountId }: { accountId: string }, { dispatch }) => { - try { - dispatch(setLoading(true)); - dispatch(setError(null)); - - const response = await getAccountTransactions(accountId, 'pending'); - dispatch(setPendingTransactions(response.txs)); - - return response.txs; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Failed to fetch pending transactions'; - dispatch(setError(errorMessage)); - throw error; - } finally { - dispatch(setLoading(false)); - } - } -); - -// Thunk for fetching confirmed transactions -export const fetchConfirmedTransactions = createAsyncThunk( - 'transaction/fetchConfirmedTransactions', - async ({ accountId }: { accountId: string }, { dispatch }) => { - try { - dispatch(setLoading(true)); - dispatch(setError(null)); - - const response = await getAccountTransactions(accountId, "success"); - dispatch(setAllTransactions(response.txs)); - - return response.txs; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Failed to fetch confirmed transactions'; - dispatch(setError(errorMessage)); - throw error; - } finally { - dispatch(setLoading(false)); - } - } -); - -// Thunk for proposing transaction with tx_bz payload -export const proposeTransactionWithTxBzThunk = createAsyncThunk( - 'transaction/proposeTransactionWithTxBz', - async ({ accountId, txBz }: { accountId: string; txBz: string }, { dispatch }) => { - try { - const payload = { - multisig_account_address: accountId, - tx_request: txBz - }; - - console.info('[TransactionAPI] Proposing transaction', { - accountId, - payloadBytes: txBz.length, - }); - - const response = await fetch(`${COORDINATOR_API_BASE_URL}/api/v1/multisig-tx/propose`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const result = await response.json(); - - // Return the full response for now - we'll update this once we know the response structure - return result; - - } catch (error) { - console.error('Error proposing transaction with tx_bz:', error); - throw error; - } - } -); - -// Create Transaction -export const createTransaction = async ( - contractId: string, - txEffect: string, - txBz: string -): Promise<{ tx_id: string }> => { - try { - const payload = { - contract_id: contractId, - tx_effect: txEffect, - tx_bz: txBz - }; - const response = await fetch(`${COORDINATOR_API_BASE_URL}/api/v1/accounts/${contractId}/transactions`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - if (response.status === 404) { - localStorage.removeItem('currentWalletId'); - document.cookie = 'currentWalletId=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'; - } - throw new Error(`HTTP error! status: ${response.status}`); - } - - const result = await response.json(); - - return result; - } catch (error) { - console.error('Error creating transaction:', error); - throw error; - } -}; - -// Thunk for creating transactions -export const createTransactionThunk = createAsyncThunk( - 'transaction/createTransaction', - async ({ accountId, txEffect, txBz }: { accountId: string; txEffect: string; txBz: string }, { dispatch }) => { - try { - const result = await createTransaction(accountId, txEffect, txBz); - - // Refresh both pending and confirmed transaction lists after creation - dispatch(fetchPendingTransactions({ accountId })); - dispatch(fetchConfirmedTransactions({ accountId })); - - return result; - } catch (error) { - throw error; - } - } -); - -// Get Consumable Notes -export const getConsumableNotes = async (address: string): Promise<{ notes: ConsumableNote[] }> => { - try { - const requestBody = { - address: address - }; - - const response = await fetch(`${COORDINATOR_API_BASE_URL}/api/v1/consumable-notes/list`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - return await response.json(); - } catch (error) { - console.error('Error getting consumable notes:', error); - throw error; - } -}; - -// Thunk for getting consumable notes with address from localStorage -export const getConsumableNotesThunk = createAsyncThunk( - 'transaction/getConsumableNotes', - async (_, { dispatch }) => { - try { - dispatch(setLoading(true)); - dispatch(setError(null)); - - // Get address from localStorage - const address = localStorage.getItem('currentWalletId'); - - if (!address) { - throw new Error('No account address found in localStorage'); - } - - const response = await getConsumableNotes(address); - return response; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Failed to get consumable notes'; - dispatch(setError(errorMessage)); - throw error; - } finally { - dispatch(setLoading(false)); - } - } -); - -// Get Transaction Stats -export const getTransactionStats = async (accountId: string): Promise => { - try { - const requestBody = { - multisig_account_address: accountId - }; - - const response = await fetch(`${COORDINATOR_API_BASE_URL}/api/v1/multisig-tx/stats`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - return await response.json(); - } catch (error) { - console.error('Error getting transaction stats:', error); - throw error; - } -}; - -// Thunk for fetching transaction stats -export const fetchTransactionStatsThunk = createAsyncThunk( - 'walletStats/fetchTransactionStats', - async ({ accountId }: { accountId: string }, { dispatch }) => { - try { - dispatch(setWalletStatsLoading(true)); - dispatch(setWalletStatsError(null)); - - const response = await getTransactionStats(accountId); - - // Set wallet stats in the walletStats slice - dispatch(setWalletStatsAction(response.tx_stats)); - - return response.tx_stats; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Failed to fetch transaction stats'; - dispatch(setWalletStatsError(errorMessage)); - throw error; - } finally { - dispatch(setWalletStatsLoading(false)); - } - } -); - -// Get Approver List -export const getApproverList = async (accountId: string): Promise => { - try { - const requestBody = { - multisig_account_address: accountId - }; - - const response = await fetch(`${COORDINATOR_API_BASE_URL}/api/v1/multisig-account/approver/list`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - return await response.json(); - } catch (error) { - console.error('Error getting approver list:', error); - throw error; - } -}; - -// Thunk for fetching approver list -export const fetchApproverListThunk = createAsyncThunk( - 'wallet/fetchApproverList', - async ({ accountId }: { accountId: string }, { dispatch }) => { - try { - dispatch(setApproversLoading(true)); - dispatch(setApproversError(null)); - - const response = await getApproverList(accountId); - - // Set approvers in the wallet slice - dispatch(setApprovers(response.approvers)); - - return response.approvers; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Failed to fetch approver list'; - dispatch(setApproversError(errorMessage)); - throw error; - } finally { - dispatch(setApproversLoading(false)); - } - } -); - -// Get Multisig Account Details -export const getMultisigAccountDetails = async (accountId: string): Promise => { - try { - const requestBody = { - multisig_account_address: accountId - }; - - const response = await fetch(`${COORDINATOR_API_BASE_URL}/api/v1/multisig-account/details`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - return await response.json(); - } catch (error) { - console.error('Error getting multisig account details:', error); - throw error; - } -}; - -// Thunk for fetching multisig account details -export const fetchMultisigAccountDetailsThunk = createAsyncThunk( - 'wallet/fetchMultisigAccountDetails', - async ({ accountId }: { accountId: string }, { dispatch }) => { - try { - dispatch(setWalletDataLoading(true)); - dispatch(setWalletDataError(null)); - - const response = await getMultisigAccountDetails(accountId); - - // Set wallet data in the wallet slice - dispatch(setWalletData(response.multisig_account)); - - return response.multisig_account; - } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Failed to fetch multisig account details'; - dispatch(setWalletDataError(errorMessage)); - throw error; - } finally { - dispatch(setWalletDataLoading(false)); - } - } -); \ No newline at end of file diff --git a/bin/coordinator-frontend/src/services/walletApi.ts b/bin/coordinator-frontend/src/services/walletApi.ts deleted file mode 100644 index 521a6bb..0000000 --- a/bin/coordinator-frontend/src/services/walletApi.ts +++ /dev/null @@ -1,88 +0,0 @@ -// API service for wallet operations -import { - CreateWalletResponse, - GetAccountInfoResponse -} from '../types'; -import { COORDINATOR_API_BASE_URL } from '../config/api'; - -export const createMultiSigWallet = async (walletData: { - walletName: string; - signatureThreshold: string; - totalSigners: string; - network: string; - signerAddresses: string[]; - signerPublicKeys: string[]; -}): Promise => { - try { - const threshold = parseInt(walletData.signatureThreshold); - const approvers = walletData.signerAddresses; - const pubKeyCommits = walletData.signerPublicKeys; - console.info('[WalletAPI] Creating multisig wallet', { - walletName: walletData.walletName, - approverCount: approvers.length, - threshold, - }); - - const base64PubKeyCommits = pubKeyCommits.map((hexPk) => Uint8Array.fromHex(hexPk).toBase64()); - - if (threshold > approvers.length) { - throw new Error(`Threshold (${threshold}) cannot be greater than total approvers (${approvers.length})`); - } - - const apiPayload = { - threshold, - approvers, - pub_key_commits: base64PubKeyCommits, - - }; - - - const response = await fetch(`${COORDINATOR_API_BASE_URL}/api/v1/multisig-account/create`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(apiPayload), - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const data = await response.json(); - return data; - } catch (error) { - console.error('Error creating multisig wallet:', error); - throw error; - } -}; - -export const getAccountInfo = async (accountId: string): Promise => { - try { - const requestBody = { - multisig_account_address: accountId - }; - - const response = await fetch(`${COORDINATOR_API_BASE_URL}/api/v1/multisig-account/details`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - if (response.status === 404) { - localStorage.removeItem('currentWalletId'); - document.cookie = 'currentWalletId=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'; - } - throw new Error(`HTTP error! status: ${response.status}`); - } - - const data = await response.json(); - return data; - } catch (error) { - console.error('Error getting account info:', error); - throw error; - } -}; \ No newline at end of file diff --git a/bin/coordinator-frontend/src/store/hooks.ts b/bin/coordinator-frontend/src/store/hooks.ts index d878cb9..dc2a0c5 100644 --- a/bin/coordinator-frontend/src/store/hooks.ts +++ b/bin/coordinator-frontend/src/store/hooks.ts @@ -2,4 +2,4 @@ import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux'; import type { RootState, AppDispatch } from './index'; export const useAppDispatch = () => useDispatch(); -export const useAppSelector: TypedUseSelectorHook = useSelector; \ No newline at end of file +export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/bin/coordinator-frontend/src/store/index.ts b/bin/coordinator-frontend/src/store/index.ts index 04ef769..2fb3a80 100644 --- a/bin/coordinator-frontend/src/store/index.ts +++ b/bin/coordinator-frontend/src/store/index.ts @@ -1,24 +1,17 @@ import { configureStore } from '@reduxjs/toolkit'; import walletReducer from './slices/walletSlice'; -import transactionReducer from './slices/transactionSlice'; -import signatureReducer from './slices/signatureSlice'; -import walletStatsReducer from './slices/walletStatsSlice'; -import { RootState, AppDispatch } from '@/types'; export const store = configureStore({ reducer: { wallet: walletReducer, - transaction: transactionReducer, - signature: signatureReducer, - walletStats: walletStatsReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { - // Ignore these action types ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'], }, }), }); -export type { RootState, AppDispatch }; \ No newline at end of file +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; diff --git a/bin/coordinator-frontend/src/store/slices/signatureSlice.ts b/bin/coordinator-frontend/src/store/slices/signatureSlice.ts deleted file mode 100644 index 9b74c64..0000000 --- a/bin/coordinator-frontend/src/store/slices/signatureSlice.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { addSignatureThunk } from '../../services/signatureApi'; - -interface SignatureState { - loading: boolean; - error: string | null; - lastSignatureResult: boolean | null; -} - -const initialState: SignatureState = { - loading: false, - error: null, - lastSignatureResult: null, -}; - -const signatureSlice = createSlice({ - name: 'signature', - initialState, - reducers: { - setLoading: (state, action: PayloadAction) => { - state.loading = action.payload; - }, - setError: (state, action: PayloadAction) => { - state.error = action.payload; - }, - clearSignatureState: (state) => { - state.loading = false; - state.error = null; - state.lastSignatureResult = null; - }, - }, - extraReducers: (builder) => { - builder - // Add signature thunk cases - .addCase(addSignatureThunk.pending, (state) => { - state.loading = true; - state.error = null; - }) - .addCase(addSignatureThunk.fulfilled, (state) => { - state.loading = false; - state.lastSignatureResult = true; - }) - .addCase(addSignatureThunk.rejected, (state, action) => { - state.loading = false; - state.error = action.error.message || 'Failed to add signature'; - state.lastSignatureResult = false; - }); - }, -}); - -export const { setLoading, setError, clearSignatureState } = signatureSlice.actions; -export default signatureSlice.reducer; \ No newline at end of file diff --git a/bin/coordinator-frontend/src/store/slices/transactionSlice.ts b/bin/coordinator-frontend/src/store/slices/transactionSlice.ts deleted file mode 100644 index 226de75..0000000 --- a/bin/coordinator-frontend/src/store/slices/transactionSlice.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { - fetchPendingTransactions, - fetchConfirmedTransactions, - createTransactionThunk, - proposeTransactionWithTxBzThunk, - getConsumableNotesThunk -} from '../../services/transactionApi'; -import { Transaction, ConsumableNote } from '../../types'; - -interface TransactionState { - pendingTransactions: Transaction[]; - allTransactions: Transaction[]; - consumableNotes: ConsumableNote[]; - loading: boolean; - error: string | null; - currentTransactionId: string | null; -} - -const initialState: TransactionState = { - pendingTransactions: [], - allTransactions: [], - consumableNotes: [], - loading: false, - error: null, - currentTransactionId: null, -}; - -const transactionSlice = createSlice({ - name: 'transaction', - initialState, - reducers: { - setPendingTransactions: (state, action: PayloadAction) => { - state.pendingTransactions = action.payload; - }, - setAllTransactions: (state, action: PayloadAction) => { - state.allTransactions = action.payload; - }, - setLoading: (state, action: PayloadAction) => { - state.loading = action.payload; - }, - setError: (state, action: PayloadAction) => { - state.error = action.payload; - }, - setCurrentTransactionId: (state, action: PayloadAction) => { - state.currentTransactionId = action.payload; - }, - clearCurrentTransactionId: (state) => { - state.currentTransactionId = null; - }, - clearPendingTransactions: (state) => { - state.pendingTransactions = []; - }, - clearAllTransactions: (state) => { - state.allTransactions = []; - }, - clearTransactions: (state) => { - state.pendingTransactions = []; - state.allTransactions = []; - state.error = null; - state.currentTransactionId = null; - }, - setConsumableNotes: (state, action: PayloadAction) => { - state.consumableNotes = action.payload; - }, - clearConsumableNotes: (state) => { - state.consumableNotes = []; - }, - }, - extraReducers: (builder) => { - builder - // Pending transactions thunk cases - .addCase(fetchPendingTransactions.pending, (state) => { - state.loading = true; - state.error = null; - }) - .addCase(fetchPendingTransactions.fulfilled, (state, action) => { - state.loading = false; - state.pendingTransactions = action.payload; - }) - .addCase(fetchPendingTransactions.rejected, (state, action) => { - state.loading = false; - state.error = action.error.message || 'Failed to fetch pending transactions'; - }) - // Confirmed transactions thunk cases - .addCase(fetchConfirmedTransactions.pending, (state) => { - state.loading = true; - state.error = null; - }) - .addCase(fetchConfirmedTransactions.fulfilled, (state, action) => { - state.loading = false; - state.allTransactions = action.payload; - }) - .addCase(fetchConfirmedTransactions.rejected, (state, action) => { - state.loading = false; - state.error = action.error.message || 'Failed to fetch confirmed transactions'; - }) - // Create transaction thunk cases - .addCase(createTransactionThunk.pending, (state) => { - state.loading = true; - state.error = null; - }) - .addCase(createTransactionThunk.fulfilled, (state, action) => { - state.loading = false; - // Store the transaction ID from the result - if (action.payload && action.payload.tx_id) { - state.currentTransactionId = action.payload.tx_id; - } - }) - .addCase(createTransactionThunk.rejected, (state, action) => { - state.loading = false; - state.error = action.error.message || 'Failed to create transaction'; - }) - // Propose transaction with tx_bz thunk cases - .addCase(proposeTransactionWithTxBzThunk.pending, (state) => { - state.loading = true; - state.error = null; - }) - .addCase(proposeTransactionWithTxBzThunk.fulfilled, (state, action) => { - state.loading = false; - // Store the transaction ID from the result - if (action.payload && action.payload.tx_id) { - state.currentTransactionId = action.payload.tx_id; - } - // Transaction list will be refreshed by other thunks - }) - .addCase(proposeTransactionWithTxBzThunk.rejected, (state, action) => { - state.loading = false; - state.error = action.error.message || 'Failed to propose transaction'; - }) - // Get consumable notes thunk cases - .addCase(getConsumableNotesThunk.pending, (state) => { - state.loading = true; - state.error = null; - }) - .addCase(getConsumableNotesThunk.fulfilled, (state, action) => { - state.loading = false; - state.consumableNotes = action.payload; - }) - .addCase(getConsumableNotesThunk.rejected, (state, action) => { - state.loading = false; - state.error = action.error.message || 'Failed to get consumable notes'; - }); - }, -}); - -export const { - setPendingTransactions, - setAllTransactions, - setLoading, - setError, - setCurrentTransactionId, - clearCurrentTransactionId, - clearPendingTransactions, - clearAllTransactions, - clearTransactions, - setConsumableNotes, - clearConsumableNotes, -} = transactionSlice.actions; - -export type { TransactionState, Transaction }; -export default transactionSlice.reducer; \ No newline at end of file diff --git a/bin/coordinator-frontend/src/store/slices/walletSlice.ts b/bin/coordinator-frontend/src/store/slices/walletSlice.ts index 0ec860c..3f9f8e6 100644 --- a/bin/coordinator-frontend/src/store/slices/walletSlice.ts +++ b/bin/coordinator-frontend/src/store/slices/walletSlice.ts @@ -1,17 +1,19 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { WalletFormData, Approver, MultisigAccount } from '../../types'; + +export interface WalletFormData { + walletName: string; + signatureThreshold: string; + totalSigners: string; + network: string; + signerAddresses: string[]; + signerPublicKeys: string[]; +} interface WalletState { formData: WalletFormData; currentStep: number; isLoading: boolean; error: string | null; - approvers: Approver[]; - approversLoading: boolean; - approversError: string | null; - walletData: MultisigAccount | null; - walletDataLoading: boolean; - walletDataError: string | null; } const initialState: WalletState = { @@ -26,12 +28,6 @@ const initialState: WalletState = { currentStep: 1, isLoading: false, error: null, - approvers: [], - approversLoading: false, - approversError: null, - walletData: null, - walletDataLoading: false, - walletDataError: null, }; const walletSlice = createSlice({ @@ -43,8 +39,7 @@ const walletSlice = createSlice({ }, updateFormField: (state, action: PayloadAction<{ field: keyof WalletFormData; value: string }>) => { const { field, value } = action.payload; - if (field === 'signerAddresses') { - // Handle signer addresses array separately + if (field === 'signerAddresses' || field === 'signerPublicKeys') { return; } (state.formData[field] as string) = value; @@ -70,7 +65,6 @@ const walletSlice = createSlice({ state.currentStep = action.payload; }, resetForm: (state) => { - state.formData = initialState.formData; state.currentStep = 1; state.error = null; @@ -81,38 +75,6 @@ const walletSlice = createSlice({ setError: (state, action: PayloadAction) => { state.error = action.payload; }, - setApprovers: (state, action: PayloadAction) => { - state.approvers = action.payload; - state.approversError = null; - }, - setApproversLoading: (state, action: PayloadAction) => { - state.approversLoading = action.payload; - }, - setApproversError: (state, action: PayloadAction) => { - state.approversError = action.payload; - state.approversLoading = false; - }, - clearApprovers: (state) => { - state.approvers = []; - state.approversError = null; - state.approversLoading = false; - }, - setWalletData: (state, action: PayloadAction) => { - state.walletData = action.payload; - state.walletDataError = null; - }, - setWalletDataLoading: (state, action: PayloadAction) => { - state.walletDataLoading = action.payload; - }, - setWalletDataError: (state, action: PayloadAction) => { - state.walletDataError = action.payload; - state.walletDataLoading = false; - }, - clearWalletData: (state) => { - state.walletData = null; - state.walletDataError = null; - state.walletDataLoading = false; - }, }, }); @@ -127,15 +89,7 @@ export const { resetForm, setLoading, setError, - setApprovers, - setApproversLoading, - setApproversError, - clearApprovers, - setWalletData, - setWalletDataLoading, - setWalletDataError, - clearWalletData, } = walletSlice.actions; -export type { WalletState, WalletFormData }; -export default walletSlice.reducer; \ No newline at end of file +export type { WalletState }; +export default walletSlice.reducer; diff --git a/bin/coordinator-frontend/src/store/slices/walletStatsSlice.ts b/bin/coordinator-frontend/src/store/slices/walletStatsSlice.ts deleted file mode 100644 index dc81198..0000000 --- a/bin/coordinator-frontend/src/store/slices/walletStatsSlice.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { TransactionStats } from '@/types'; - -interface WalletStatsState { - stats: TransactionStats | null; - loading: boolean; - error: string | null; -} - -const initialState: WalletStatsState = { - stats: null, - loading: false, - error: null, -}; - -const walletStatsSlice = createSlice({ - name: 'walletStats', - initialState, - reducers: { - setWalletStats: (state, action: PayloadAction) => { - state.stats = action.payload; - state.error = null; - }, - setLoading: (state, action: PayloadAction) => { - state.loading = action.payload; - }, - setError: (state, action: PayloadAction) => { - state.error = action.payload; - state.loading = false; - }, - clearWalletStats: (state) => { - state.stats = null; - state.error = null; - state.loading = false; - }, - }, -}); - -export const { setWalletStats, setLoading, setError, clearWalletStats } = walletStatsSlice.actions; -export default walletStatsSlice.reducer; - diff --git a/bin/coordinator-frontend/src/stubs/empty.js b/bin/coordinator-frontend/src/stubs/empty.js new file mode 100644 index 0000000..6b1a452 --- /dev/null +++ b/bin/coordinator-frontend/src/stubs/empty.js @@ -0,0 +1,2 @@ +// Empty stub for optional Para wallet connector modules +module.exports = {}; diff --git a/bin/coordinator-frontend/src/types/components.ts b/bin/coordinator-frontend/src/types/components.ts index f4ea26b..969a4d8 100644 --- a/bin/coordinator-frontend/src/types/components.ts +++ b/bin/coordinator-frontend/src/types/components.ts @@ -13,85 +13,23 @@ export interface TaskBarProps { export interface PendingActionsProps { threshold?: number; - fixedHeight?: boolean; // If true, shows fixed height for 5 items (home page), if false/undefined, shows scrollable (transactions page) + fixedHeight?: boolean; } export interface RecentTransactionsProps { threshold: number; - fixedHeight?: boolean; // If true, shows fixed height for 5 items (home page), if false/undefined, shows scrollable (transactions page) -} - -export interface MultisigSetupProps { - className?: string; -} - -// Transaction Components -export interface DecodedTransaction { - id: string; - status: string; - signature_count: number; - type: string; - transactionType: "sent" | "received" | null; - recipient: string; - amount: number; - currency: string; - priority: string; - memo: string; - timestamp: number | null; - created_at: string; - tx_summary?: string; - tx_request?: string; - input_note_ids?: Array<{ note_id: string; note_id_file_bytes: string }>; + fixedHeight?: boolean; } // Asset Components -export interface TokenHoldingsProps { - fungibleAssets: FungibleAsset[]; - isLoading: boolean; -} - -// Wallet Components -export interface WalletConnectionProps { - className?: string; -} - -export interface WalletInfoProps { - className?: string; +export interface FungibleAsset { + faucetId: string; + balance: string; } -export interface WalletStatusProps { - className?: string; -} - -export interface WalletTransactionProps { - className?: string; -} - -export interface ConnectWalletModalProps { - isOpen: boolean; - onClose: () => void; - secretKey: string; - setSecretKey: (key: string) => void; - approverAddress: string; - setApproverAddress: (address: string) => void; - onConnect: () => void; -} - -export interface TransferBoxProps { - transaction: Transaction; - index: number; - threshold?: number; - onApprove?: (txId: string) => void; - isSelected?: boolean; - onSelect?: (txId: string) => void; -} - -// Interaction Components -export interface InitiateFundTransferProps { - onCancel?: () => void; +export interface TokenHoldingsProps { fungibleAssets: FungibleAsset[]; isLoading: boolean; - onOpen?: () => Promise; } // Notification Types @@ -99,7 +37,3 @@ export interface Notification { type: "success" | "error"; message: string; } - -// Import types -import { FungibleAsset } from '../hooks/useFungibleAssets'; -import { Transaction } from './transaction'; diff --git a/bin/coordinator-frontend/src/types/hooks.ts b/bin/coordinator-frontend/src/types/hooks.ts deleted file mode 100644 index 9a100f5..0000000 --- a/bin/coordinator-frontend/src/types/hooks.ts +++ /dev/null @@ -1,110 +0,0 @@ -// Hook-related types - -// Wallet Data Hook Types -export interface WalletFormData { - walletName: string; - signatureThreshold: string; - totalSigners: string; - network: string; - signerAddresses: string[]; - signerPublicKeys: string[]; -} - -export interface WalletData { - approver_number: number; - kind: string; - threshold: number; - approver: string[]; - // Form data from localStorage - walletFormData?: WalletFormData; -} - -// Fungible Assets Hook Types -export interface FungibleAsset { - faucetId: string; - balance: string; -} - -// Miden SDK Types -export interface MidenClient { - create_wallet: (storageMode: StorageMode) => Promise; - get_accounts: () => Promise; - sync: () => Promise; - new_transaction: (accountId: string, transactionType: string) => Promise; -} - -export interface MidenWallet { - account_id: string; - address: string; -} - -export interface MidenAccount { - id: string; - address: string; -} - -export interface MidenTransaction { - id: string; - to_bytes: () => Uint8Array; -} - -export interface StorageMode { - type: 'memory' | 'sqlite'; - path?: string; -} - -export interface MidenFaucet { - account_id: string; - asset_id: string; -} - -// WebClient types from miden-sdk -export interface WebClient { - syncState: () => Promise; - getInputNote: (noteId: string) => Promise; - importNoteFile: (noteBytes: Uint8Array) => Promise; - consumeNotes: (noteIds: string[], accountId: string) => Promise; - getInputNotes: (status?: string) => Promise; - newTransaction: (accountId: string, transactionType: string) => Promise; -} - -export interface InputNoteRecord { - note_id: string; - assets: NoteAsset[]; - status: string; - inclusion_proof?: string; -} - -export interface NoteAsset { - asset_type: string; - faucet_id?: string; - amount: bigint; -} - -export interface TransactionTemplate { - add_input_note: (noteId: string) => void; - send_asset: (assetId: string, amount: bigint, senderAccountId: string, recipientAddress: string, recallHeight: bigint) => void; - build: () => Promise; -} - -// Miden SDK Hook Types -export interface MidenSdkContextState { - isLoading: boolean; - Miden: typeof MidenSDK | null; - createClient: () => Promise; -} - -// Miden SDK class type -export interface MidenSDK { - Client: { - new(config: unknown): MidenClient; - }; - StorageMode: { - Memory: () => StorageMode; - Sqlite: (path: string) => StorageMode; - }; -} - -export interface MidenSdkProviderProps { - children: React.ReactNode; -} diff --git a/bin/coordinator-frontend/src/types/index.ts b/bin/coordinator-frontend/src/types/index.ts index bda4f06..8225170 100644 --- a/bin/coordinator-frontend/src/types/index.ts +++ b/bin/coordinator-frontend/src/types/index.ts @@ -1,8 +1,4 @@ // Export all types from a single location -export * from './wallet'; -export * from './transaction'; -export * from './signature'; export * from './components'; -export * from './hooks'; export * from './store'; -export * from './notes'; \ No newline at end of file +export * from './psm'; diff --git a/bin/coordinator-frontend/src/types/notes.ts b/bin/coordinator-frontend/src/types/notes.ts deleted file mode 100644 index 32ece1c..0000000 --- a/bin/coordinator-frontend/src/types/notes.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Note-related types -export interface Note { - note_id: string; - note_type: string; - sender: string; - tag: string; - num_assets: number; - status: string; - created_at?: string; -} - -export interface ConsumableNote extends Note { - assets: NoteAsset[]; - inclusion_proof?: string; -} - -export interface NoteAsset { - asset_id: string; - amount: string; -} - -export interface NoteIdData { - note_id: string; - note_id_file_bytes: string; -} - -export interface GetConsumableNotesResponse { - notes: ConsumableNote[]; -} - diff --git a/bin/coordinator-frontend/src/types/psm.ts b/bin/coordinator-frontend/src/types/psm.ts new file mode 100644 index 0000000..6959559 --- /dev/null +++ b/bin/coordinator-frontend/src/types/psm.ts @@ -0,0 +1,20 @@ +import type { AuthSecretKey } from '@miden-sdk/miden-sdk'; +import type { SignatureScheme } from '@openzeppelin/miden-multisig-client'; + +export type { WalletSource, ExternalWalletState } from '@/wallets/types'; + +export interface SignerKeyInfo { + commitment: string; + secretKey: AuthSecretKey; +} + +export interface SignerInfo { + falcon: SignerKeyInfo; + ecdsa: SignerKeyInfo; + activeScheme: SignatureScheme; +} + +export interface OtherSigner { + id: string; + commitment: string; +} diff --git a/bin/coordinator-frontend/src/types/signature.ts b/bin/coordinator-frontend/src/types/signature.ts deleted file mode 100644 index 385b10b..0000000 --- a/bin/coordinator-frontend/src/types/signature.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Signature-related types -export interface AddSignatureRequest { - tx_id: string; - approver: string; - signature: string; -} \ No newline at end of file diff --git a/bin/coordinator-frontend/src/types/store.ts b/bin/coordinator-frontend/src/types/store.ts index a3be981..5d730c6 100644 --- a/bin/coordinator-frontend/src/types/store.ts +++ b/bin/coordinator-frontend/src/types/store.ts @@ -1,16 +1,6 @@ // Store/State management types -import { Transaction } from './transaction'; +// WalletFormData is now defined in store/slices/walletSlice.ts -// Transaction State -export interface TransactionState { - pendingTransactions: Transaction[]; - allTransactions: Transaction[]; - currentTransactionId: string | null; - loading: boolean; - error: string | null; -} - -// Wallet Form Data Type export interface WalletFormData { walletName: string; signatureThreshold: string; @@ -20,65 +10,12 @@ export interface WalletFormData { signerPublicKeys: string[]; } -// Wallet State export interface WalletState { - formData: WalletFormData | null; + formData: WalletFormData; currentStep: number; isLoading: boolean; error: string | null; - approvers: Array<{ - address: string; - pub_key_commit: string; - created_at: string; - updated_at: string; - }>; - approversLoading: boolean; - approversError: string | null; - walletData: { - address: string; - kind: string; - threshold: number; - created_at: string; - updated_at: string; - } | null; - walletDataLoading: boolean; - walletDataError: string | null; -} - -// Signature Data Type -export interface SignatureData { - tx_id: string; - approver: string; - signature: string; - created_at?: string; -} - -// Signature State -export interface SignatureState { - signatures: SignatureData[]; - loading: boolean; - error: string | null; -} - -// Wallet Stats State -export interface WalletStatsState { - stats: { - total: number; - last_month: number; - total_success: number; - } | null; - loading: boolean; - error: string | null; } -// Root State and Dispatch Types -export type RootState = { - wallet: WalletState; - transaction: TransactionState; - signature: SignatureState; - walletStats: WalletStatsState; -}; - -// AppDispatch will be typed from the store -// Using a simpler type here to avoid circular dependencies -export type AppDispatch = (action: unknown) => void; +// RootState and AppDispatch are now exported from store/index.ts +export type { RootState, AppDispatch } from '@/store/index'; diff --git a/bin/coordinator-frontend/src/types/transaction.ts b/bin/coordinator-frontend/src/types/transaction.ts deleted file mode 100644 index a8fb16f..0000000 --- a/bin/coordinator-frontend/src/types/transaction.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Transaction-related types -export interface InputNoteId { - note_id: string; - note_id_file_bytes: string; -} - -export interface Transaction { - id: string; - multisig_account_address: string; - status: string; - tx_request: string; - tx_summary: string; - tx_summary_commit: string; - signature_count: number; - created_at: string; - updated_at: string; - input_note_ids?: InputNoteId[]; -} - -export interface GetAccountTransactionsResponse { - txs: Transaction[]; -} - -export interface CreateTransactionRequest { - contract_id: string; - tx_effect: string; - tx_bz: string; -} - -export interface TransactionStats { - total: number; - last_month: number; - total_success: number; -} - -export interface GetTransactionStatsResponse { - tx_stats: TransactionStats; -} - -export interface Approver { - address: string; - pub_key_commit: string; - created_at: string; - updated_at: string; -} - -export interface GetApproverListResponse { - approvers: Approver[]; -} - -export interface MultisigAccount { - address: string; - kind: string; - threshold: number; - created_at: string; - updated_at: string; -} - -export interface GetMultisigAccountDetailsResponse { - multisig_account: MultisigAccount; -} \ No newline at end of file diff --git a/bin/coordinator-frontend/src/types/wallet.ts b/bin/coordinator-frontend/src/types/wallet.ts deleted file mode 100644 index 932287f..0000000 --- a/bin/coordinator-frontend/src/types/wallet.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Wallet-related types -export interface WalletFormData { - walletName: string; - signatureThreshold: string; - totalSigners: string; - network: string; - signerAddresses: string[]; - signerPublicKeys: string[]; -} - -export interface CreateWalletRequest { - threshold: number; - approvers: string[]; - pub_key_commits: string[]; -} - -export interface CreateWalletResponse { - address: string; - created_at: string; - updated_at: string; -} - -export interface GetAccountInfoResponse { - multisig_account: { - address: string; - kind: string; - threshold: number; - created_at: string; - updated_at: string; - }; -} \ No newline at end of file diff --git a/bin/coordinator-frontend/src/wallets/index.ts b/bin/coordinator-frontend/src/wallets/index.ts new file mode 100644 index 0000000..d2479d1 --- /dev/null +++ b/bin/coordinator-frontend/src/wallets/index.ts @@ -0,0 +1 @@ +export type { WalletSource, ExternalWalletState } from './types'; diff --git a/bin/coordinator-frontend/src/wallets/types.ts b/bin/coordinator-frontend/src/wallets/types.ts new file mode 100644 index 0000000..036c6c3 --- /dev/null +++ b/bin/coordinator-frontend/src/wallets/types.ts @@ -0,0 +1,11 @@ +import type { SignatureScheme } from '@openzeppelin/miden-multisig-client'; + +export type WalletSource = 'local' | 'para' | 'miden-wallet'; + +export interface ExternalWalletState { + source: WalletSource; + connected: boolean; + publicKey: string | null; + commitment: string | null; + scheme: SignatureScheme | null; +} diff --git a/bin/coordinator-server/src/payload.rs b/bin/coordinator-server/src/payload.rs index b9311cd..a876b84 100644 --- a/bin/coordinator-server/src/payload.rs +++ b/bin/coordinator-server/src/payload.rs @@ -127,7 +127,7 @@ impl From for MultisigTxPayload { .tx_request(tx_request.to_bytes()) .tx_summary(tx_summary.to_bytes()) .tx_summary_commit(tx_summary_commit.to_bytes()) - .input_note_ids(tx_request.get_input_note_ids().into_iter().map(From::from).collect()) + .input_note_ids(tx_request.input_note_ids().map(From::from).collect()) .maybe_signature_count(signature_count) .created_at(aux.created_at()) .updated_at(aux.updated_at()) diff --git a/bin/coordinator-server/src/routes.rs b/bin/coordinator-server/src/routes.rs index ad692ad..4ad052b 100644 --- a/bin/coordinator-server/src/routes.rs +++ b/bin/coordinator-server/src/routes.rs @@ -164,7 +164,7 @@ pub async fn add_signature( })? .ok_or(AppError::InvalidNetworkId)?; - let Signature::RpoFalcon512(signature) = + let Signature::Falcon512Rpo(signature) = Signature::read_from_bytes(&signature).map_err(|_| AppError::InvalidSignature)? else { return Err(AppError::InvalidSignature); diff --git a/crates/coordinator/engine/Cargo.toml b/crates/coordinator/engine/Cargo.toml index 711f5b2..e8d9477 100644 --- a/crates/coordinator/engine/Cargo.toml +++ b/crates/coordinator/engine/Cargo.toml @@ -23,8 +23,8 @@ url = { workspace = true } [dev-dependencies] diesel = { features = ["postgres"], version = "2" } diesel_migrations = "2" -miden-client = { features = ["tonic"], version = "0.12" } -miden-client-sqlite-store = "0.12" +miden-client = { features = ["tonic"], version = "0.13" } +miden-client-sqlite-store = "0.13" openssl-sys = { features = ["vendored"], version = "0.9" } pq-sys = { features = ["bundled"], version = "0.7" } rand = "0.9" diff --git a/crates/coordinator/engine/src/multisig_client_runtime.rs b/crates/coordinator/engine/src/multisig_client_runtime.rs index f515864..33d2491 100644 --- a/crates/coordinator/engine/src/multisig_client_runtime.rs +++ b/crates/coordinator/engine/src/multisig_client_runtime.rs @@ -297,11 +297,15 @@ where let signatures = signatures .into_iter() - .map(|s| s.map(|s| Signature::RpoFalcon512(s).to_prepared_signature(Word::empty()))) + .map(|s| s.map(|s| Signature::Falcon512Rpo(s).to_prepared_signature(Word::empty()))) .collect(); + let account = account_record + .try_into() + .map_err(|e: miden_client::ClientError| MultisigClientRuntimeError::other(e.to_string()))?; + let tx_result = client - .submit_new_multisig_transaction(account_record.into(), tx_request, tx_summary, signatures) + .submit_new_multisig_transaction(account, tx_request, tx_summary, signatures) .await; let _ = sender diff --git a/crates/coordinator/engine/tests/multisig_engine_test.rs b/crates/coordinator/engine/tests/multisig_engine_test.rs index 1a812d0..dd2485b 100644 --- a/crates/coordinator/engine/tests/multisig_engine_test.rs +++ b/crates/coordinator/engine/tests/multisig_engine_test.rs @@ -15,9 +15,10 @@ use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; use miden_client::{ Client, DebugMode, Felt, account::{ - Account, AccountBuilder, AccountStorageMode, AccountType, NetworkId, - component::{AuthRpoFalcon512, BasicFungibleFaucet, BasicWallet}, + Account, AccountBuilder, AccountStorageMode, AccountType, + component::{AuthFalcon512Rpo, BasicFungibleFaucet, BasicWallet}, }, + address::NetworkId, asset::{FungibleAsset, TokenSymbol}, auth::AuthSecretKey, builder::ClientBuilder, @@ -37,7 +38,6 @@ use miden_multisig_coordinator_engine::{ response::{CreateMultisigAccountResponseDissolved, ProposeMultisigTxResponseDissolved}, }; use miden_multisig_coordinator_store::MultisigStore; -use rand::{RngCore, rngs::StdRng}; use tempfile::TempDir; use testcontainers::{ContainerAsync, ImageExt, runners::AsyncRunner}; use testcontainers_modules::postgres::Postgres; @@ -79,7 +79,7 @@ async fn single_note_consumption_works_using_multisig_engine_to_get_consumable_n tokio::time::sleep(Duration::from_secs(5)).await; - let engine = start_testnet_multisig_engine(&temp_dir.join("multisig")).await; + let engine = start_devnet_multisig_engine(&temp_dir.join("multisig")).await; let approvers = vec![alice_account.id(), bob_account.id(), charlie_account.id()]; @@ -111,15 +111,15 @@ async fn single_note_consumption_works_using_multisig_engine_to_get_consumable_n tokio::time::sleep(Duration::from_secs(5)).await; let consume_notes_tx_request = { - let note_ids: Vec<_> = engine + let notes: Vec<_> = engine .get_consumable_notes(GetConsumableNotesRequest::builder().build()) .await .unwrap() .into_iter() - .map(|(nr, _)| nr.id()) + .map(|(nr, _)| nr.try_into().unwrap()) .collect(); - TransactionRequestBuilder::new().build_consume_notes(note_ids).unwrap() + TransactionRequestBuilder::new().build_consume_notes(notes).unwrap() }; let propose_request = ProposeMultisigTxRequest::builder() @@ -156,7 +156,7 @@ async fn single_note_consumption_works_using_multisig_engine_to_get_consumable_n assert!(tx_result.is_some()); let asset_balance = { - let (mut client, _) = setup_testnet_client(&temp_dir.join("external")).await; + let (mut client, _) = setup_devnet_client(&temp_dir.join("external")).await; client.import_account_by_id(multisig_account.id()).await.unwrap(); client.sync_state().await.unwrap(); @@ -164,7 +164,8 @@ async fn single_note_consumption_works_using_multisig_engine_to_get_consumable_n let imported_multisig_account_record = client.get_account(multisig_account.id()).await.unwrap().unwrap(); - let imported_multisig_account = imported_multisig_account_record.account(); + let imported_multisig_account: Account = + imported_multisig_account_record.try_into().unwrap(); imported_multisig_account.vault().get_balance(ff_account.id()).unwrap() }; @@ -177,21 +178,20 @@ async fn setup_fungible_faucet_client( symbol: &str, decimals: u8, max_supply: u64, -) -> (Client>, Account) { - let (mut client, keystore) = setup_testnet_client(temp_dir).await; +) -> (Client, Account) { + let (mut client, keystore) = setup_devnet_client(temp_dir).await; - let mut init_seed = [0u8; 32]; - client.rng().fill_bytes(&mut init_seed); + let init_seed: [u8; 32] = rand::random(); let symbol = TokenSymbol::new(symbol).unwrap(); let max_supply = Felt::new(max_supply); - let sk = AuthSecretKey::new_rpo_falcon512(); + let sk = AuthSecretKey::new_falcon512_rpo(); let account = AccountBuilder::new(init_seed) .account_type(AccountType::FungibleFaucet) - .storage_mode(miden_client::account::AccountStorageMode::Public) - .with_auth_component(AuthRpoFalcon512::new(sk.public_key().to_commitment())) + .storage_mode(AccountStorageMode::Public) + .with_auth_component(AuthFalcon512Rpo::new(sk.public_key().to_commitment())) .with_component(BasicFungibleFaucet::new(symbol, decimals, max_supply).unwrap()) .build() .unwrap(); @@ -204,18 +204,17 @@ async fn setup_fungible_faucet_client( async fn setup_regular_account_client( temp_dir: &Path, -) -> (Client>, Account, SecretKey) { - let (mut client, keystore) = setup_testnet_client(temp_dir).await; +) -> (Client, Account, SecretKey) { + let (mut client, keystore) = setup_devnet_client(temp_dir).await; - let mut init_seed = [0u8; 32]; - client.rng().fill_bytes(&mut init_seed); + let init_seed: [u8; 32] = rand::random(); - let sk = AuthSecretKey::new_rpo_falcon512(); + let sk = AuthSecretKey::new_falcon512_rpo(); let account = AccountBuilder::new(init_seed) .account_type(AccountType::RegularAccountUpdatableCode) .storage_mode(AccountStorageMode::Public) - .with_auth_component(AuthRpoFalcon512::new(sk.public_key().to_commitment())) + .with_auth_component(AuthFalcon512Rpo::new(sk.public_key().to_commitment())) .with_component(BasicWallet) .build() .unwrap(); @@ -223,23 +222,21 @@ async fn setup_regular_account_client( client.add_account(&account, false).await.unwrap(); keystore.add_key(&sk).unwrap(); - let AuthSecretKey::RpoFalcon512(sk) = sk else { - panic!("secret key must be rpo falcon 512 scheme"); + let AuthSecretKey::Falcon512Rpo(sk) = sk else { + panic!("secret key must be falcon 512 rpo scheme"); }; (client, account, sk) } -async fn setup_testnet_client( - temp_dir: &Path, -) -> (Client>, FilesystemKeyStore) { +async fn setup_devnet_client(temp_dir: &Path) -> (Client, FilesystemKeyStore) { let keystore = FilesystemKeyStore::new(temp_dir.join("keystore")).expect("failed to initialize keystore"); let client = ClientBuilder::new() - .grpc_client(&Endpoint::testnet(), Some(20_000)) + .grpc_client(&Endpoint::devnet(), Some(20_000)) .sqlite_store(temp_dir.join("store")) - .authenticator(keystore.clone().into()) + .filesystem_keystore(temp_dir.join("keystore").to_str().unwrap()) .in_debug_mode(DebugMode::Enabled) .build() .await @@ -248,7 +245,7 @@ async fn setup_testnet_client( (client, keystore) } -async fn start_testnet_multisig_engine(temp_dir: &Path) -> MultisigEngine { +async fn start_devnet_multisig_engine(temp_dir: &Path) -> MultisigEngine { let db_url = setup_test_db().await; let multisig_store = @@ -257,10 +254,10 @@ async fn start_testnet_multisig_engine(temp_dir: &Path) -> MultisigEngine MultisigClient where AUTH: TransactionAuthenticator + Sync + 'static, { - const CONFIG_STORAGE_SLOT_INDEX: u8 = 0; - - const PUB_KEYS_STORAGE_SLOT_INDEX: u8 = 1; - const NUM_APPROVERS_INDEX: usize = 1; /// Propose a multisig transaction. @@ -125,7 +121,7 @@ where let num_approvers: u32 = { let felt = *account .storage() - .get_item(Self::CONFIG_STORAGE_SLOT_INDEX) + .get_item(AuthFalcon512RpoMultisig::threshold_config_slot()) .map_err(|e| { TransactionExecutionError::StorageSlotIndexOutOfBounds(e.to_string().into()) })? @@ -154,7 +150,10 @@ where let pub_key_index_word = Word::from([Felt::from(i), ZERO, ZERO, ZERO]); account .storage() - .get_map_item(Self::PUB_KEYS_STORAGE_SLOT_INDEX, pub_key_index_word) + .get_map_item( + AuthFalcon512RpoMultisig::approver_public_keys_slot(), + pub_key_index_word, + ) .map_err(|_| TransactionExecutionError::PubKeyStorageSlotMap)? }; Rpo256::merge(&[pub_key, msg]) diff --git a/crates/miden-multisig-client/src/tests.rs b/crates/miden-multisig-client/src/tests.rs index 12ef87d..1e51686 100644 --- a/crates/miden-multisig-client/src/tests.rs +++ b/crates/miden-multisig-client/src/tests.rs @@ -1,10 +1,11 @@ use alloc::boxed::Box; use miden_client::{ - auth::SigningInputs, + auth::{AuthSchemeId, SigningInputs}, + keystore::FilesystemKeyStore, note::NoteType, testing::{ - common::{self, TestClientKeyStore}, + common::{self}, mock::MockRpcApi, }, transaction::TransactionRequestBuilder, @@ -12,9 +13,7 @@ use miden_client::{ use super::*; -const AUTH_SCHEME_ID: u8 = 0; - -type TestMultisigClient = MultisigClient; +type TestMultisigClient = MultisigClient; #[tokio::test] async fn multisig() { @@ -30,7 +29,7 @@ async fn multisig() { &mut signer_a_client, AccountStorageMode::Private, &authenticator_a, - AUTH_SCHEME_ID, + AuthSchemeId::Falcon512Rpo, ) .await .unwrap(); @@ -40,7 +39,7 @@ async fn multisig() { &mut signer_b_client, AccountStorageMode::Private, &authenticator_b, - AUTH_SCHEME_ID, + AuthSchemeId::Falcon512Rpo, ) .await .unwrap(); @@ -56,7 +55,7 @@ async fn multisig() { coordinator_client.deref_mut(), AccountStorageMode::Public, &coordinator_keystore, - AUTH_SCHEME_ID, + AuthSchemeId::Falcon512Rpo, ) .await .unwrap(); @@ -76,7 +75,7 @@ async fn multisig() { coordinator_client.sync_state().await.unwrap(); coordinator_client - .import_note(miden_client::note::NoteFile::NoteId(note.id())) + .import_notes(&[miden_client::note::NoteFile::NoteId(note.id())]) .await .unwrap(); @@ -84,7 +83,7 @@ async fn multisig() { let salt = Word::empty(); let tx_request = TransactionRequestBuilder::new() .auth_arg(salt) - .build_consume_notes(vec![note.id()]) + .build_consume_notes(vec![note]) .unwrap(); // Propose the transaction (should fail with Unauthorized) @@ -119,7 +118,7 @@ async fn multisig() { assert!(tx_result.is_ok()); } -async fn setup_multisig_client() -> (TestMultisigClient, MockRpcApi, TestClientKeyStore) { +async fn setup_multisig_client() -> (TestMultisigClient, MockRpcApi, FilesystemKeyStore) { let (client, mock_rpc_api, keystore) = miden_multisig_test_utils::create_test_client(std::env::temp_dir()).await; diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 99c6dc4..f6e8f8e 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -10,5 +10,5 @@ workspace = true [dependencies] miden-client = { features = ["testing"], workspace = true } miden-client-sqlite-store = { workspace = true } -miden-testing = "0.12" +miden-testing = "0.13" rand = { workspace = true } diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 35f07ac..5025f63 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -13,17 +13,17 @@ use miden_client::{ builder::ClientBuilder, crypto::RpoRandomCoin, keystore::FilesystemKeyStore, - note::{NoteExecutionMode, NoteTag, NoteType}, + note::{NoteTag, NoteType}, testing::{ NoteBuilder, - common::{TestClientKeyStore, create_test_store_path}, + common::create_test_store_path, mock::{MockClient, MockRpcApi}, }, transaction::OutputNote, }; use miden_client_sqlite_store::SqliteStore; use miden_testing::{MockChain, MockChainBuilder}; -use rand::{Rng, rngs::StdRng}; +use rand::Rng; // HELPERS // ================================================================================================ @@ -38,7 +38,7 @@ use rand::{Rng, rngs::StdRng}; /// The `keystore_path` controls where keys are stored on disk during the test run. pub async fn create_test_client

( keystore_path: P, -) -> (MockClient>, MockRpcApi, FilesystemKeyStore) +) -> (MockClient, MockRpcApi, FilesystemKeyStore) where P: AsRef, { @@ -51,7 +51,7 @@ where async fn create_test_client_builder

( keystore_path: P, -) -> (ClientBuilder, MockRpcApi, FilesystemKeyStore) +) -> (ClientBuilder, MockRpcApi, FilesystemKeyStore) where P: AsRef, { @@ -87,7 +87,7 @@ async fn create_prebuilt_mock_chain() -> MockChain { let note_first = NoteBuilder::new(mock_account.id(), RpoRandomCoin::new([0, 0, 0, 0].map(Felt::new).into())) - .tag(NoteTag::for_public_use_case(0, 0, NoteExecutionMode::Local).unwrap().into()) + .tag(NoteTag::new(0).into()) .build() .unwrap(); mock_chain_builder.add_output_note(OutputNote::Full(note_first)); @@ -95,7 +95,7 @@ async fn create_prebuilt_mock_chain() -> MockChain { let note_second = NoteBuilder::new(mock_account.id(), RpoRandomCoin::new([0, 0, 0, 1].map(Felt::new).into())) .note_type(NoteType::Private) - .tag(NoteTag::for_local_use_case(0, 0).unwrap().into()) + .tag(NoteTag::new(0).into()) .build() .unwrap(); mock_chain_builder.add_output_note(OutputNote::Full(note_second.clone()));