diff --git a/CMakeLists.txt b/CMakeLists.txt index cd8ee12..67643ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,8 @@ logos_module( src/LEZWalletAccountModel.cpp src/LEZAccountFilterModel.h src/LEZAccountFilterModel.cpp + src/LEZClaimableAccountFilterModel.h + src/LEZClaimableAccountFilterModel.cpp FIND_PACKAGES Qt6Gui LINK_LIBRARIES diff --git a/flake.lock b/flake.lock index b59b5a5..9ff99ae 100644 --- a/flake.lock +++ b/flake.lock @@ -251,11 +251,11 @@ "logos-module-builder": "logos-module-builder_8" }, "locked": { - "lastModified": 1778182598, - "narHash": "sha256-QoyirL/blmVM2cmXQwbR6rmHufQJ6vaJSCkNORu4qaA=", + "lastModified": 1780946924, + "narHash": "sha256-8wFILW1oMS1L77Ff4bcC/5W9dytNe6I1rhf5AHhQW2U=", "owner": "logos-co", "repo": "logos-capability-module", - "rev": "e675e9e3a98ee69bb303365c2c626f9237bc1ab5", + "rev": "0013e7653eb1b23d5495e39f149259bd60dc1db4", "type": "github" }, "original": { @@ -339,11 +339,11 @@ "logos-module-builder": "logos-module-builder_9" }, "locked": { - "lastModified": 1778182598, - "narHash": "sha256-QoyirL/blmVM2cmXQwbR6rmHufQJ6vaJSCkNORu4qaA=", + "lastModified": 1781129198, + "narHash": "sha256-vnJCkDQlGKXStnXFGhATzY3dXwwQT4/nnljReFmAzQc=", "owner": "logos-co", "repo": "logos-capability-module", - "rev": "e675e9e3a98ee69bb303365c2c626f9237bc1ab5", + "rev": "5d438961e2defe72b76028150787d59e586eec8c", "type": "github" }, "original": { @@ -1421,11 +1421,11 @@ ] }, "locked": { - "lastModified": 1781897505, - "narHash": "sha256-sRVgfo8VfWOXb3IH2JqZQAqBSfL0bYo3FrFSgS6yvqk=", + "lastModified": 1782292503, + "narHash": "sha256-XKjPeGtv07xufTeUQJWST5vOznSVn1ykFQR3IxDbFto=", "owner": "logos-co", "repo": "logos-cpp-sdk", - "rev": "aea29d37975aa0c32fdf37d7119830be905acf34", + "rev": "31b9d2bf5f2b7542df50d07c1050b865b4b30de8", "type": "github" }, "original": { @@ -1583,6 +1583,7 @@ "logos-cpp-sdk_32": { "inputs": { "logos-nix": "logos-nix_322", + "logos-protocol": "logos-protocol_11", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -1593,11 +1594,11 @@ ] }, "locked": { - "lastModified": 1779716125, - "narHash": "sha256-PfpmvkXyDnQRW0dcnIthGKE17rm5bwm8KLbEqJcm9kM=", + "lastModified": 1781313016, + "narHash": "sha256-gbchE6ExNqonj5XXVURXahYaaML0qAtSV9wHHMjs+/E=", "owner": "logos-co", "repo": "logos-cpp-sdk", - "rev": "d77c3dd616384addfcc8c5607860466850fdfcf2", + "rev": "87abcd804422cbd733c2e658ae2f42c20162115c", "type": "github" }, "original": { @@ -1731,6 +1732,13 @@ "logos-cpp-sdk_37": { "inputs": { "logos-nix": "logos-nix_366", + "logos-protocol": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-protocol" + ], "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -1742,11 +1750,11 @@ ] }, "locked": { - "lastModified": 1779716125, - "narHash": "sha256-PfpmvkXyDnQRW0dcnIthGKE17rm5bwm8KLbEqJcm9kM=", + "lastModified": 1781304979, + "narHash": "sha256-uCCoJJRVCUUO6j03GO6/02+uuLd9GaAi/0dHaiW2bXs=", "owner": "logos-co", "repo": "logos-cpp-sdk", - "rev": "d77c3dd616384addfcc8c5607860466850fdfcf2", + "rev": "f0fe8cbfeb1fe62d934b4abf8c300047a49420c9", "type": "github" }, "original": { @@ -1764,6 +1772,7 @@ "logos-view-module-runtime", "logos-nix" ], + "logos-protocol": "logos-protocol_14", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -1775,11 +1784,11 @@ ] }, "locked": { - "lastModified": 1779716125, - "narHash": "sha256-PfpmvkXyDnQRW0dcnIthGKE17rm5bwm8KLbEqJcm9kM=", + "lastModified": 1781313016, + "narHash": "sha256-gbchE6ExNqonj5XXVURXahYaaML0qAtSV9wHHMjs+/E=", "owner": "logos-co", "repo": "logos-cpp-sdk", - "rev": "d77c3dd616384addfcc8c5607860466850fdfcf2", + "rev": "87abcd804422cbd733c2e658ae2f42c20162115c", "type": "github" }, "original": { @@ -2179,11 +2188,11 @@ ] }, "locked": { - "lastModified": 1778224414, - "narHash": "sha256-QSSCCE4eKGiIS5PqHDfk9Lj4VFmeCh18khbFWrGxvvc=", + "lastModified": 1780947586, + "narHash": "sha256-0l5Xjbx9bLRc6LF0FU3LZ15RmEYK9oOPy/EaOxQYHkc=", "owner": "logos-co", "repo": "logos-design-system", - "rev": "6176f0d7a5dfeb64a7f0f98e7ca2bf71a4804772", + "rev": "4a37b34446ef4e55b16a5aeb011ffc5e8cbcbbb8", "type": "github" }, "original": { @@ -2237,17 +2246,17 @@ "rust-rapidsnark": "rust-rapidsnark" }, "locked": { - "lastModified": 1782215423, - "narHash": "sha256-KkVt9SWYIe+z7rQgxIN86bSShgvdj4KlnSOKFyUbLNU=", + "lastModified": 1782310268, + "narHash": "sha256-tIIIO+V+w/C/KLtKnLIv8O8/GoJ4PzgJSWrlQCXJ2P4=", "owner": "logos-blockchain", - "repo": "lssa", - "rev": "4d055c71a77d4b3aa147dfe8e1673afb3217840b", + "repo": "logos-execution-zone", + "rev": "fb8cbac40e0bda4f152415ff4f181cdc6bca6d4a", "type": "github" }, "original": { "owner": "logos-blockchain", - "repo": "lssa", - "rev": "4d055c71a77d4b3aa147dfe8e1673afb3217840b", + "repo": "logos-execution-zone", + "rev": "fb8cbac40e0bda4f152415ff4f181cdc6bca6d4a", "type": "github" } }, @@ -2565,6 +2574,8 @@ "logos-module": "logos-module_50", "logos-nix": "logos-nix_368", "logos-package-manager": "logos-package-manager_18", + "logos-protocol": "logos-protocol_12", + "logos-qt-sdk": "logos-qt-sdk_9", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -2576,11 +2587,11 @@ "process-stats": "process-stats_10" }, "locked": { - "lastModified": 1779724404, - "narHash": "sha256-HKgY1cbcm1BgFau0KBJba0pKGNZ/7oJMcqJMhPDQ20Q=", + "lastModified": 1781311549, + "narHash": "sha256-1laKvOevI1RrXQxllvlXFJnFmRQWLbvWfmbUZ02O7NY=", "owner": "logos-co", "repo": "logos-liblogos", - "rev": "be7c8fd8e89e9c7506421735e18dc85bde0c1cb2", + "rev": "050f2d36284d29e0660a4086b5a8af49fd70f4ff", "type": "github" }, "original": { @@ -3168,11 +3179,11 @@ "rust-overlay": "rust-overlay_3" }, "locked": { - "lastModified": 1782146416, - "narHash": "sha256-WkkDTc8p0YkZ5H1w9KmYih0WFT1+k/yjPKXtM3Mt6+E=", + "lastModified": 1782297704, + "narHash": "sha256-7BtfWuFsBvZ9dBcF2LjZttXhHzt/3tuHI7TJ4p+w2E0=", "owner": "logos-co", "repo": "logos-module-builder", - "rev": "f4942648c28127d4ef4d4b467b22bd33873ab4fa", + "rev": "0325d6aa9d5737fca461482c204f594339da24e2", "type": "github" }, "original": { @@ -4137,11 +4148,11 @@ ] }, "locked": { - "lastModified": 1781311603, - "narHash": "sha256-Ym3i52f5MWuZy8Qas3+zfoxz+mxQUsyW+Qol1no6kDs=", + "lastModified": 1781873810, + "narHash": "sha256-GDUq1PFeN24RmXAcb7l2GH9YyjPQeLxg0hfeKOUxcmo=", "owner": "logos-co", "repo": "logos-module", - "rev": "a3e288a71d6f79445db598b0ffcca2ee435596b9", + "rev": "7583396720a33153f4bec85c1464bc463fb95477", "type": "github" }, "original": { @@ -4631,11 +4642,11 @@ ] }, "locked": { - "lastModified": 1776369033, - "narHash": "sha256-ehePoUEd/u3Ng0TvCmjocXYJWWH6P61PA7tNpgV59lo=", + "lastModified": 1781310867, + "narHash": "sha256-Ym3i52f5MWuZy8Qas3+zfoxz+mxQUsyW+Qol1no6kDs=", "owner": "logos-co", "repo": "logos-module", - "rev": "194778491ef4dd36967ac40cc2fec6b8a8b1d660", + "rev": "b42805dac2874d8f72e1c12f7ac6a92ca7e5452c", "type": "github" }, "original": { @@ -10255,11 +10266,11 @@ "nixpkgs": "nixpkgs_376" }, "locked": { - "lastModified": 1773955630, - "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", "owner": "logos-co", "repo": "logos-nix", - "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", "type": "github" }, "original": { @@ -10327,11 +10338,11 @@ "nixpkgs": "nixpkgs_380" }, "locked": { - "lastModified": 1774455309, - "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", "owner": "logos-co", "repo": "logos-nix", - "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { @@ -10345,11 +10356,11 @@ "nixpkgs": "nixpkgs_381" }, "locked": { - "lastModified": 1773955630, - "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", "owner": "logos-co", "repo": "logos-nix", - "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", "type": "github" }, "original": { @@ -10381,11 +10392,11 @@ "nixpkgs": "nixpkgs_382" }, "locked": { - "lastModified": 1773955630, - "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", "owner": "logos-co", "repo": "logos-nix", - "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", "type": "github" }, "original": { @@ -10399,11 +10410,11 @@ "nixpkgs": "nixpkgs_383" }, "locked": { - "lastModified": 1774455309, - "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", "owner": "logos-co", "repo": "logos-nix", - "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { @@ -10417,11 +10428,11 @@ "nixpkgs": "nixpkgs_384" }, "locked": { - "lastModified": 1774455309, - "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", "owner": "logos-co", "repo": "logos-nix", - "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { @@ -10435,11 +10446,11 @@ "nixpkgs": "nixpkgs_385" }, "locked": { - "lastModified": 1773955630, - "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", "owner": "logos-co", "repo": "logos-nix", - "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", "type": "github" }, "original": { @@ -10453,11 +10464,11 @@ "nixpkgs": "nixpkgs_386" }, "locked": { - "lastModified": 1773955630, - "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", "owner": "logos-co", "repo": "logos-nix", - "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", "type": "github" }, "original": { @@ -10471,11 +10482,11 @@ "nixpkgs": "nixpkgs_387" }, "locked": { - "lastModified": 1774455309, - "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", "owner": "logos-co", "repo": "logos-nix", - "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { @@ -10489,11 +10500,11 @@ "nixpkgs": "nixpkgs_388" }, "locked": { - "lastModified": 1774455309, - "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", "owner": "logos-co", "repo": "logos-nix", - "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { @@ -10507,11 +10518,11 @@ "nixpkgs": "nixpkgs_389" }, "locked": { - "lastModified": 1773955630, - "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", "owner": "logos-co", "repo": "logos-nix", - "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", "type": "github" }, "original": { @@ -10525,11 +10536,11 @@ "nixpkgs": "nixpkgs_390" }, "locked": { - "lastModified": 1773955630, - "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", "owner": "logos-co", "repo": "logos-nix", - "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", "type": "github" }, "original": { @@ -10597,11 +10608,11 @@ "nixpkgs": "nixpkgs_393" }, "locked": { - "lastModified": 1774455309, - "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", "owner": "logos-co", "repo": "logos-nix", - "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { @@ -10633,11 +10644,11 @@ "nixpkgs": "nixpkgs_395" }, "locked": { - "lastModified": 1773955630, - "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", "owner": "logos-co", "repo": "logos-nix", - "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", "type": "github" }, "original": { @@ -10651,11 +10662,11 @@ "nixpkgs": "nixpkgs_396" }, "locked": { - "lastModified": 1774455309, - "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", "owner": "logos-co", "repo": "logos-nix", - "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { @@ -10687,11 +10698,11 @@ "nixpkgs": "nixpkgs_398" }, "locked": { - "lastModified": 1773955630, - "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", "owner": "logos-co", "repo": "logos-nix", - "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", "type": "github" }, "original": { @@ -10705,11 +10716,11 @@ "nixpkgs": "nixpkgs_399" }, "locked": { - "lastModified": 1774455309, - "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", "owner": "logos-co", "repo": "logos-nix", - "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { @@ -10741,11 +10752,11 @@ "nixpkgs": "nixpkgs_401" }, "locked": { - "lastModified": 1773955630, - "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", "owner": "logos-co", "repo": "logos-nix", - "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", "type": "github" }, "original": { @@ -10790,6 +10801,42 @@ "type": "github" } }, + "logos-nix_400": { + "inputs": { + "nixpkgs": "nixpkgs_402" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_401": { + "inputs": { + "nixpkgs": "nixpkgs_403" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, "logos-nix_41": { "inputs": { "nixpkgs": "nixpkgs_41" @@ -12298,7 +12345,7 @@ }, "logos-package-manager_19": { "inputs": { - "logos-nix": "logos-nix_386", + "logos-nix": "logos-nix_388", "logos-package": "logos-package_45", "nix-bundle-appimage": "nix-bundle-appimage_19", "nix-bundle-dir": "nix-bundle-dir_64", @@ -12312,11 +12359,11 @@ ] }, "locked": { - "lastModified": 1775836847, - "narHash": "sha256-pU7GShEizE8HkDGvR9NWZPqksiGyvfcWodtFyc318TM=", + "lastModified": 1782134133, + "narHash": "sha256-65w2mEijAjUPVntS3kUKZygjRkZoj7dgCYtk8lD5jL8=", "owner": "logos-co", "repo": "logos-package-manager", - "rev": "39118d6c52226e88a77c6ff7d1196229f56b757e", + "rev": "4871ed198d6e35f198af15c93afd184aa36788e0", "type": "github" }, "original": { @@ -13679,7 +13726,7 @@ }, "logos-package_43": { "inputs": { - "logos-nix": "logos-nix_379", + "logos-nix": "logos-nix_381", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -13706,7 +13753,7 @@ }, "logos-package_44": { "inputs": { - "logos-nix": "logos-nix_383", + "logos-nix": "logos-nix_385", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -13732,7 +13779,7 @@ }, "logos-package_45": { "inputs": { - "logos-nix": "logos-nix_387", + "logos-nix": "logos-nix_389", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -13744,11 +13791,11 @@ ] }, "locked": { - "lastModified": 1775835037, - "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "lastModified": 1781873979, + "narHash": "sha256-Z5XUSB9wfdrmQhG/0fehJeBd9CjrlIEeO+mKDhE46H4=", "owner": "logos-co", "repo": "logos-package", - "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "rev": "c207525d30f48620696668c38486884bad5384bc", "type": "github" }, "original": { @@ -13759,7 +13806,7 @@ }, "logos-package_46": { "inputs": { - "logos-nix": "logos-nix_392", + "logos-nix": "logos-nix_394", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -13771,11 +13818,11 @@ ] }, "locked": { - "lastModified": 1775835037, - "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "lastModified": 1781872378, + "narHash": "sha256-ZHh+krtTbn6Qt7Hm3+shrhAAGCFa7FXt8ajYqx+Nt1o=", "owner": "logos-co", "repo": "logos-package", - "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "rev": "ab885df3641e9adb61ff16e42d3e6f63b24db8f6", "type": "github" }, "original": { @@ -13786,7 +13833,7 @@ }, "logos-package_47": { "inputs": { - "logos-nix": "logos-nix_395", + "logos-nix": "logos-nix_397", "nixpkgs": [ "logos_execution_zone", "nix-bundle-lgx", @@ -13811,7 +13858,7 @@ }, "logos-package_48": { "inputs": { - "logos-nix": "logos-nix_398", + "logos-nix": "logos-nix_400", "nixpkgs": [ "nix-bundle-lgx", "logos-package", @@ -14540,11 +14587,11 @@ ] }, "locked": { - "lastModified": 1782082589, - "narHash": "sha256-Qeqxp0HYb3oTpmfD5YlFPJzpAJa7Ilb9o4sMeVvmHRI=", + "lastModified": 1782291447, + "narHash": "sha256-Jkc8MOuJNuQEEs69GMOwDL1bzAPxdMsJ7hlEUz6nkQM=", "owner": "logos-co", "repo": "logos-protocol", - "rev": "315a3a2e0af61bc47aad5601ee44cd7689975820", + "rev": "c6234940fc030af2fd5c445db82120dc2162887f", "type": "github" }, "original": { @@ -14554,6 +14601,161 @@ } }, "logos-protocol_11": { + "inputs": { + "logos-nix": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk", + "logos-nix" + ], + "nixpkgs": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk", + "logos-protocol", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781303997, + "narHash": "sha256-j9U4wPlHdN5oA+65h1SAo/s2YUHvNYKxij4/pCqEtAs=", + "owner": "logos-co", + "repo": "logos-protocol", + "rev": "9de4165ab68ece4071647026cb2596bed8a8d7e2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-protocol", + "type": "github" + } + }, + "logos-protocol_12": { + "inputs": { + "logos-nix": "logos-nix_374", + "nixpkgs": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-protocol", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781303997, + "narHash": "sha256-j9U4wPlHdN5oA+65h1SAo/s2YUHvNYKxij4/pCqEtAs=", + "owner": "logos-co", + "repo": "logos-protocol", + "rev": "9de4165ab68ece4071647026cb2596bed8a8d7e2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-protocol", + "type": "github" + } + }, + "logos-protocol_13": { + "inputs": { + "logos-nix": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-nix" + ], + "nixpkgs": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781303997, + "narHash": "sha256-j9U4wPlHdN5oA+65h1SAo/s2YUHvNYKxij4/pCqEtAs=", + "owner": "logos-co", + "repo": "logos-protocol", + "rev": "9de4165ab68ece4071647026cb2596bed8a8d7e2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-protocol", + "type": "github" + } + }, + "logos-protocol_14": { + "inputs": { + "logos-nix": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-view-module-runtime", + "logos-cpp-sdk", + "logos-nix" + ], + "nixpkgs": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-view-module-runtime", + "logos-cpp-sdk", + "logos-protocol", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781303997, + "narHash": "sha256-j9U4wPlHdN5oA+65h1SAo/s2YUHvNYKxij4/pCqEtAs=", + "owner": "logos-co", + "repo": "logos-protocol", + "rev": "9de4165ab68ece4071647026cb2596bed8a8d7e2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-protocol", + "type": "github" + } + }, + "logos-protocol_15": { + "inputs": { + "logos-nix": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-view-module-runtime", + "logos-nix" + ], + "nixpkgs": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-view-module-runtime", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781303997, + "narHash": "sha256-j9U4wPlHdN5oA+65h1SAo/s2YUHvNYKxij4/pCqEtAs=", + "owner": "logos-co", + "repo": "logos-protocol", + "rev": "9de4165ab68ece4071647026cb2596bed8a8d7e2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-protocol", + "type": "github" + } + }, + "logos-protocol_16": { "inputs": { "logos-nix": [ "logos_execution_zone", @@ -14569,11 +14771,11 @@ ] }, "locked": { - "lastModified": 1781065710, - "narHash": "sha256-QmNQ7LdrfzyvsZGISGKJKbHqB03F6JExgGF2K1f2WcE=", + "lastModified": 1781303997, + "narHash": "sha256-j9U4wPlHdN5oA+65h1SAo/s2YUHvNYKxij4/pCqEtAs=", "owner": "logos-co", "repo": "logos-protocol", - "rev": "ca28190272eef2772edf9b3e1df47e121f02a726", + "rev": "9de4165ab68ece4071647026cb2596bed8a8d7e2", "type": "github" }, "original": { @@ -15039,7 +15241,7 @@ }, "logos-qt-mcp_9": { "inputs": { - "logos-nix": "logos-nix_376", + "logos-nix": "logos-nix_378", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -15050,11 +15252,11 @@ ] }, "locked": { - "lastModified": 1774455349, - "narHash": "sha256-rebrtH1UxC1hDuwQBwyYbGzNCrnuuqiVL7OvzUhk65k=", + "lastModified": 1781077555, + "narHash": "sha256-rkqva3DbhhxWrVp6KLyhVxbAK2MLIWfUcPG5HM7wnsQ=", "owner": "logos-co", "repo": "logos-qt-mcp", - "rev": "c5223b4b640add09e461983b8fddbd12c8b31f4f", + "rev": "c87f6bd985a07ec2809081c8d0830108aa895819", "type": "github" }, "original": { @@ -15096,6 +15298,133 @@ "type": "github" } }, + "logos-qt-sdk_10": { + "inputs": { + "logos-cpp-sdk": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk" + ], + "logos-nix": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-nix" + ], + "logos-protocol": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-protocol" + ], + "nixpkgs": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781306785, + "narHash": "sha256-Z8nAECXYUx4mt0fH9Q9IFpLlAZc+m/Z43GD8mZEIoKE=", + "owner": "logos-co", + "repo": "logos-qt-sdk", + "rev": "8d0e5d98990261407b50600d886158a1f78e1b1c", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-qt-sdk", + "type": "github" + } + }, + "logos-qt-sdk_11": { + "inputs": { + "logos-cpp-sdk": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-view-module-runtime", + "logos-cpp-sdk" + ], + "logos-nix": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-view-module-runtime", + "logos-nix" + ], + "logos-protocol": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-view-module-runtime", + "logos-protocol" + ], + "nixpkgs": [ + "logos_execution_zone", + "logos-module-builder", + "logos-standalone-app", + "logos-view-module-runtime", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781306785, + "narHash": "sha256-Z8nAECXYUx4mt0fH9Q9IFpLlAZc+m/Z43GD8mZEIoKE=", + "owner": "logos-co", + "repo": "logos-qt-sdk", + "rev": "8d0e5d98990261407b50600d886158a1f78e1b1c", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-qt-sdk", + "type": "github" + } + }, + "logos-qt-sdk_12": { + "inputs": { + "logos-cpp-sdk": [ + "logos_execution_zone", + "logos-module-builder", + "logos-test-framework", + "logos-cpp-sdk" + ], + "logos-nix": [ + "logos_execution_zone", + "logos-module-builder", + "logos-test-framework", + "logos-nix" + ], + "logos-protocol": [ + "logos_execution_zone", + "logos-module-builder", + "logos-test-framework", + "logos-protocol" + ], + "nixpkgs": [ + "logos_execution_zone", + "logos-module-builder", + "logos-test-framework", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781306785, + "narHash": "sha256-Z8nAECXYUx4mt0fH9Q9IFpLlAZc+m/Z43GD8mZEIoKE=", + "owner": "logos-co", + "repo": "logos-qt-sdk", + "rev": "8d0e5d98990261407b50600d886158a1f78e1b1c", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-qt-sdk", + "type": "github" + } + }, "logos-qt-sdk_2": { "inputs": { "logos-cpp-sdk": [ @@ -15353,11 +15682,11 @@ ] }, "locked": { - "lastModified": 1781962335, - "narHash": "sha256-TThOj0o115vPawAPJgv5zhDnV/hVfnDqjpga9Z2SlPU=", + "lastModified": 1782296555, + "narHash": "sha256-1M/TzEn/1Whby0eGlaovwHQQ1eiCXSJ/AOMFihoOHSI=", "owner": "logos-co", "repo": "logos-qt-sdk", - "rev": "cdf077cdb296b39fe45d6f774a0b4ae71be48d45", + "rev": "9020fa99f214458fd1f1c69a8ed16e02536ac9f9", "type": "github" }, "original": { @@ -15371,34 +15700,34 @@ "logos-cpp-sdk": [ "logos_execution_zone", "logos-module-builder", - "logos-test-framework", + "logos-standalone-app", + "logos-liblogos", "logos-cpp-sdk" ], - "logos-nix": [ - "logos_execution_zone", - "logos-module-builder", - "logos-test-framework", - "logos-nix" - ], + "logos-nix": "logos-nix_375", "logos-protocol": [ "logos_execution_zone", "logos-module-builder", - "logos-test-framework", + "logos-standalone-app", + "logos-liblogos", "logos-protocol" ], "nixpkgs": [ "logos_execution_zone", "logos-module-builder", - "logos-test-framework", + "logos-standalone-app", + "logos-liblogos", + "logos-qt-sdk", + "logos-nix", "nixpkgs" ] }, "locked": { - "lastModified": 1781066423, - "narHash": "sha256-3pVeJ/cK2z4OEi5iLORjoC7YpdrRrHmigl9SyvEapI8=", + "lastModified": 1781306785, + "narHash": "sha256-Z8nAECXYUx4mt0fH9Q9IFpLlAZc+m/Z43GD8mZEIoKE=", "owner": "logos-co", "repo": "logos-qt-sdk", - "rev": "355774603877637b486ea0aeefbf7828ae321868", + "rev": "8d0e5d98990261407b50600d886158a1f78e1b1c", "type": "github" }, "original": { @@ -15714,8 +16043,10 @@ "logos-cpp-sdk": "logos-cpp-sdk_32", "logos-design-system": "logos-design-system_8", "logos-liblogos": "logos-liblogos_9", - "logos-nix": "logos-nix_375", + "logos-nix": "logos-nix_377", + "logos-protocol": "logos-protocol_13", "logos-qt-mcp": "logos-qt-mcp_9", + "logos-qt-sdk": "logos-qt-sdk_10", "logos-view-module-runtime": "logos-view-module-runtime_9", "nix-bundle-lgx": "nix-bundle-lgx_25", "nixpkgs": [ @@ -15727,11 +16058,11 @@ ] }, "locked": { - "lastModified": 1779725202, - "narHash": "sha256-7IqnkRG1CkOKfiw5qf1o+Kg9ci+MKlcJNXgrg6GyYXk=", + "lastModified": 1781319979, + "narHash": "sha256-7rbkfuCeHmhv+eVqWUeVzJqtNDW9ynjf5TkajNIySDU=", "owner": "logos-co", "repo": "logos-standalone-app", - "rev": "f812b43f73f3c264ee0f8a8354f40721431d6846", + "rev": "c8c0bece1fe8f0d21541e136b222aecbe2755b9f", "type": "github" }, "original": { @@ -16114,9 +16445,9 @@ "logos-module-builder", "logos-cpp-sdk" ], - "logos-nix": "logos-nix_381", - "logos-protocol": "logos-protocol_11", - "logos-qt-sdk": "logos-qt-sdk_9", + "logos-nix": "logos-nix_383", + "logos-protocol": "logos-protocol_16", + "logos-qt-sdk": "logos-qt-sdk_12", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -16126,11 +16457,11 @@ ] }, "locked": { - "lastModified": 1781118884, - "narHash": "sha256-bM8Deqf6soZeUqWy3RdyY0APRKLWmjD/78g1fbEObaY=", + "lastModified": 1781319474, + "narHash": "sha256-dcJJymaY43fBCluUocVZHpDuUwO4Inm/vGnlO880j/0=", "owner": "logos-co", "repo": "logos-test-framework", - "rev": "5b8fcc9ffdc5c66511a95a6bf6b41dd74fbbacc9", + "rev": "9665bc792905527553cfaed1e27ce10cf5e812a4", "type": "github" }, "original": { @@ -16436,7 +16767,9 @@ "logos-view-module-runtime_9": { "inputs": { "logos-cpp-sdk": "logos-cpp-sdk_38", - "logos-nix": "logos-nix_377", + "logos-nix": "logos-nix_379", + "logos-protocol": "logos-protocol_15", + "logos-qt-sdk": "logos-qt-sdk_11", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -16447,11 +16780,11 @@ ] }, "locked": { - "lastModified": 1779723114, - "narHash": "sha256-6NNZG9FTaLRKwJZxtFWySlfiTanh25AmJK5Ru+S9HrM=", + "lastModified": 1781316066, + "narHash": "sha256-s5aaSGhu5HiotCXa0Vbjgp17TTMXedt52cqD/8FM4aQ=", "owner": "logos-co", "repo": "logos-view-module-runtime", - "rev": "21dddc380eca36e7e865cf5a437f63e0e16f30d3", + "rev": "7541c318d85cabed61b9222d59d4110836ede430", "type": "github" }, "original": { @@ -16467,16 +16800,17 @@ "nix-bundle-lgx": "nix-bundle-lgx_28" }, "locked": { - "lastModified": 1782295438, - "narHash": "sha256-DtnSt7EbxwAvmSYR2hTI76dKd6egROgy7xHt8RMOuZE=", + "lastModified": 1782397060, + "narHash": "sha256-IBnUVkRZKQ029m1DLw7ctLDJpOJzl0bKsDzzxRD9d4U=", "owner": "logos-blockchain", "repo": "logos-execution-zone-module", - "rev": "5537c0065eee0abf098bb53a2caf108aa3e94d10", + "rev": "b27f7b4e600e9041694e8cd29c03579ccdab9fbb", "type": "github" }, "original": { "owner": "logos-blockchain", "repo": "logos-execution-zone-module", + "rev": "b27f7b4e600e9041694e8cd29c03579ccdab9fbb", "type": "github" } }, @@ -16796,7 +17130,7 @@ }, "nix-bundle-appimage_19": { "inputs": { - "logos-nix": "logos-nix_388", + "logos-nix": "logos-nix_390", "nix-bundle-dir": "nix-bundle-dir_63", "nixpkgs": [ "logos_execution_zone", @@ -18788,7 +19122,7 @@ }, "nix-bundle-dir_61": { "inputs": { - "logos-nix": "logos-nix_380", + "logos-nix": "logos-nix_382", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -18815,7 +19149,7 @@ }, "nix-bundle-dir_62": { "inputs": { - "logos-nix": "logos-nix_384", + "logos-nix": "logos-nix_386", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -18841,7 +19175,7 @@ }, "nix-bundle-dir_63": { "inputs": { - "logos-nix": "logos-nix_389", + "logos-nix": "logos-nix_391", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -18867,7 +19201,7 @@ }, "nix-bundle-dir_64": { "inputs": { - "logos-nix": "logos-nix_390", + "logos-nix": "logos-nix_392", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -18894,7 +19228,7 @@ }, "nix-bundle-dir_65": { "inputs": { - "logos-nix": "logos-nix_393", + "logos-nix": "logos-nix_395", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -18906,11 +19240,11 @@ ] }, "locked": { - "lastModified": 1774455641, - "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "lastModified": 1776974030, + "narHash": "sha256-lQ/tc/owFa7Yjh8MXe0Fn2GodQXVGmpOZwe5gBEcENA=", "owner": "logos-co", "repo": "nix-bundle-dir", - "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "rev": "4937262f55cf8be942263255dd0801e3e3878bc9", "type": "github" }, "original": { @@ -18921,7 +19255,7 @@ }, "nix-bundle-dir_66": { "inputs": { - "logos-nix": "logos-nix_396", + "logos-nix": "logos-nix_398", "nixpkgs": [ "logos_execution_zone", "nix-bundle-lgx", @@ -18946,7 +19280,7 @@ }, "nix-bundle-dir_67": { "inputs": { - "logos-nix": "logos-nix_399", + "logos-nix": "logos-nix_401", "nixpkgs": [ "nix-bundle-lgx", "nix-bundle-dir", @@ -19598,7 +19932,7 @@ }, "nix-bundle-lgx_25": { "inputs": { - "logos-nix": "logos-nix_378", + "logos-nix": "logos-nix_380", "logos-package": "logos-package_43", "nix-bundle-dir": "nix-bundle-dir_61", "nixpkgs": [ @@ -19626,7 +19960,7 @@ }, "nix-bundle-lgx_26": { "inputs": { - "logos-nix": "logos-nix_382", + "logos-nix": "logos-nix_384", "logos-package": "logos-package_44", "nix-bundle-dir": "nix-bundle-dir_62", "nixpkgs": [ @@ -19653,7 +19987,7 @@ }, "nix-bundle-lgx_27": { "inputs": { - "logos-nix": "logos-nix_391", + "logos-nix": "logos-nix_393", "logos-package": "logos-package_46", "nix-bundle-dir": "nix-bundle-dir_65", "nixpkgs": [ @@ -19666,11 +20000,11 @@ ] }, "locked": { - "lastModified": 1775836380, - "narHash": "sha256-XbBPcMuDFA/SxYVw9TIRQbhie5Vj5MqwdU+Gh1iR1LA=", + "lastModified": 1781872587, + "narHash": "sha256-zl9odZ1Tq7QBAYJVrXiLSC47FVYra3kIakb/TeGPoZI=", "owner": "logos-co", "repo": "nix-bundle-lgx", - "rev": "9d8f8602b1574ec9ac4c9b31ae0c92570221c268", + "rev": "b49074a8e1157832002b11d3d254c1aaa4b96680", "type": "github" }, "original": { @@ -19681,7 +20015,7 @@ }, "nix-bundle-lgx_28": { "inputs": { - "logos-nix": "logos-nix_394", + "logos-nix": "logos-nix_396", "logos-package": "logos-package_47", "nix-bundle-dir": "nix-bundle-dir_66", "nixpkgs": [ @@ -19707,7 +20041,7 @@ }, "nix-bundle-lgx_29": { "inputs": { - "logos-nix": "logos-nix_397", + "logos-nix": "logos-nix_399", "logos-package": "logos-package_48", "nix-bundle-dir": "nix-bundle-dir_67", "nixpkgs": [ @@ -20176,7 +20510,7 @@ }, "nix-bundle-logos-module-install_9": { "inputs": { - "logos-nix": "logos-nix_385", + "logos-nix": "logos-nix_387", "logos-package-manager": "logos-package-manager_19", "nix-bundle-lgx": "nix-bundle-lgx_27", "nixpkgs": [ @@ -20188,11 +20522,11 @@ ] }, "locked": { - "lastModified": 1775839388, - "narHash": "sha256-0QH146bzL2kKBYJq2yA35iPwug55j2xjEyKCS7tjhvg=", + "lastModified": 1782134900, + "narHash": "sha256-v5A+e+1Sk1BXbOkaPkc9Pc2BOozCN1cH4onkcV/jnUQ=", "owner": "logos-co", "repo": "nix-bundle-logos-module-install", - "rev": "89cc9ea91275396d589c767d76926459ac77ef20", + "rev": "55de9a6fce755387224ececd0493f46b028ee0a3", "type": "github" }, "original": { @@ -25593,6 +25927,38 @@ "type": "github" } }, + "nixpkgs_402": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_403": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs_41": { "locked": { "lastModified": 1759036355, @@ -26648,7 +27014,7 @@ }, "process-stats_10": { "inputs": { - "logos-nix": "logos-nix_374", + "logos-nix": "logos-nix_376", "nixpkgs": [ "logos_execution_zone", "logos-module-builder", @@ -26972,11 +27338,11 @@ ] }, "locked": { - "lastModified": 1782012058, - "narHash": "sha256-9mWUnReOUXfKjZuJAL/bAFH3LUyECTRtXgSNVjRw3UY=", + "lastModified": 1782271078, + "narHash": "sha256-Ukpyd+syDTr+ky+nI+SbUnWySfiqNRzA3gzFZYWepeg=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "8534567325bd8a8d2928e6afd81e0a87d19efd3c", + "rev": "b7286019daa89e4e192c8913c3ec7002976034d0", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index d10bc20..31d2f5d 100644 --- a/flake.nix +++ b/flake.nix @@ -4,7 +4,7 @@ inputs = { logos-module-builder.url = "github:logos-co/logos-module-builder"; nix-bundle-lgx.url = "github:logos-co/nix-bundle-lgx"; - logos_execution_zone.url = "github:logos-blockchain/logos-execution-zone-module"; + logos_execution_zone.url = "github:logos-blockchain/logos-execution-zone-module?rev=b27f7b4e600e9041694e8cd29c03579ccdab9fbb"; }; outputs = inputs@{ logos-module-builder, ... }: diff --git a/src/LEZClaimableAccountFilterModel.cpp b/src/LEZClaimableAccountFilterModel.cpp new file mode 100644 index 0000000..c4648e4 --- /dev/null +++ b/src/LEZClaimableAccountFilterModel.cpp @@ -0,0 +1,19 @@ +#include "LEZClaimableAccountFilterModel.h" + +LEZClaimableAccountFilterModel::LEZClaimableAccountFilterModel(QObject* parent) + : QSortFilterProxyModel(parent) +{ + connect(this, &QAbstractItemModel::rowsInserted, this, &LEZClaimableAccountFilterModel::countChanged); + connect(this, &QAbstractItemModel::rowsRemoved, this, &LEZClaimableAccountFilterModel::countChanged); + connect(this, &QAbstractItemModel::modelReset, this, &LEZClaimableAccountFilterModel::countChanged); + connect(this, &QAbstractItemModel::layoutChanged, this, &LEZClaimableAccountFilterModel::countChanged); +} + +bool LEZClaimableAccountFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const +{ + if (!sourceModel()) + return false; + const QModelIndex idx = sourceModel()->index(sourceRow, 0, sourceParent); + const QString vaultBalance = sourceModel()->data(idx, LEZWalletAccountModel::VaultBalanceRole).toString(); + return !vaultBalance.isEmpty() && vaultBalance != QStringLiteral("0"); +} diff --git a/src/LEZClaimableAccountFilterModel.h b/src/LEZClaimableAccountFilterModel.h new file mode 100644 index 0000000..df01e6a --- /dev/null +++ b/src/LEZClaimableAccountFilterModel.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include "LEZWalletAccountModel.h" + +// Filters the account model down to accounts with a nonzero claimable vault balance +// (see LEZWalletBackend::refreshVaultBalances), for the Bridge tab's Claim Deposit list. +class LEZClaimableAccountFilterModel : public QSortFilterProxyModel { + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + +public: + explicit LEZClaimableAccountFilterModel(QObject* parent = nullptr); + + int count() const { return rowCount(); } + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; + +signals: + void countChanged(); +}; diff --git a/src/LEZWalletAccountModel.cpp b/src/LEZWalletAccountModel.cpp index 9eab1ee..308a1e8 100644 --- a/src/LEZWalletAccountModel.cpp +++ b/src/LEZWalletAccountModel.cpp @@ -1,5 +1,8 @@ #include "LEZWalletAccountModel.h" -#include +#include +#include +#include +#include LEZWalletAccountModel::LEZWalletAccountModel(QObject* parent) : QAbstractListModel(parent) @@ -23,7 +26,11 @@ QVariant LEZWalletAccountModel::data(const QModelIndex& index, int role) const case NameRole: return e.name; case AccountIdRole: return e.accountId; case BalanceRole: return e.balance; + case VaultBalanceRole: return e.vaultBalance; case IsPublicRole: return e.isPublic; + case SectionKeyRole: return e.sectionKey; + case KeysJsonRole: return e.keysJson; + case IsFirstInGroupRole: return e.isFirstInGroup; default: return QVariant(); } } @@ -34,53 +41,63 @@ QHash LEZWalletAccountModel::roleNames() const { NameRole, "name" }, { AccountIdRole, "accountId" }, { BalanceRole, "balance" }, - { IsPublicRole, "isPublic" } + { VaultBalanceRole, "vaultBalance" }, + { IsPublicRole, "isPublic" }, + { SectionKeyRole, "sectionKey" }, + { KeysJsonRole, "keysJson" }, + { IsFirstInGroupRole, "isFirstInGroup" } }; } -void LEZWalletAccountModel::replaceFromJsonArray(const QJsonArray& arr) -{ - beginResetModel(); - int oldCount = m_entries.size(); - m_entries.clear(); - for (const QJsonValue& v : arr) { - LEZWalletAccountEntry e; - e.balance = QString(); - if (v.isObject()) { - const QJsonObject obj = v.toObject(); - e.accountId = obj.value(QStringLiteral("account_id")).toString(); - e.isPublic = obj.value(QStringLiteral("is_public")).toBool(true); - } else { - e.accountId = v.toString(); - e.isPublic = true; - } - e.name = QString(); - m_entries.append(e); - } - endResetModel(); - if (oldCount != m_entries.size()) - emit countChanged(); -} - void LEZWalletAccountModel::replaceFromVariantList(const QVariantList& list) { + // Rebuilding from scratch loses any balance/vaultBalance already fetched for an + // account that's still present — carry those over so periodic re-listing (e.g. to + // pick up newly discovered private accounts) doesn't make the claimable list and + // balances flicker empty until the next refresh repopulates them. + QHash> previousBalances; + previousBalances.reserve(m_entries.size()); + for (const LEZWalletAccountEntry& e : m_entries) + previousBalances.insert(e.accountId, qMakePair(e.balance, e.vaultBalance)); + beginResetModel(); int oldCount = m_entries.size(); m_entries.clear(); for (const QVariant& v : list) { LEZWalletAccountEntry e; e.balance = QString(); - if (v.canConvert()) { - const QVariantMap obj = v.toMap(); - e.accountId = obj.value(QStringLiteral("account_id")).toString(); - e.isPublic = obj.value(QStringLiteral("is_public"), true).toBool(); + e.name = QString(); + if (v.type() == QVariant::Map) { + const QVariantMap map = v.toMap(); + e.accountId = map.value(QStringLiteral("account_id")).toString(); + e.isPublic = map.value(QStringLiteral("is_public"), true).toBool(); + if (e.isPublic) { + e.sectionKey = PublicSectionKey; + } else { + e.sectionKey = map.value(QStringLiteral("npk")).toString(); + e.keysJson = map.value(QStringLiteral("keys_json")).toString(); + } } else { e.accountId = v.toString(); e.isPublic = true; + e.sectionKey = PublicSectionKey; + } + const auto previous = previousBalances.find(e.accountId); + if (previous != previousBalances.end()) { + e.balance = previous->first; + e.vaultBalance = previous->second; } - e.name = QString(); m_entries.append(e); } + // Keep entries grouped by section (public first) so consecutive rows of the same + // group are contiguous, then mark each group's first row for the QML header. + std::stable_sort(m_entries.begin(), m_entries.end(), + [](const LEZWalletAccountEntry& a, const LEZWalletAccountEntry& b) { + if (a.isPublic != b.isPublic) return a.isPublic; + return a.sectionKey < b.sectionKey; + }); + for (int i = 0; i < m_entries.size(); ++i) + m_entries[i].isFirstInGroup = (i == 0) || (m_entries[i].sectionKey != m_entries[i - 1].sectionKey); endResetModel(); if (oldCount != m_entries.size()) emit countChanged(); @@ -99,3 +116,26 @@ void LEZWalletAccountModel::setBalanceByAccountId(const QString& accountId, cons } } } + +void LEZWalletAccountModel::setVaultBalanceByAccountId(const QString& accountId, const QString& vaultBalance) +{ + for (int i = 0; i < m_entries.size(); ++i) { + if (m_entries.at(i).accountId == accountId) { + if (m_entries.at(i).vaultBalance != vaultBalance) { + m_entries[i].vaultBalance = vaultBalance; + QModelIndex idx = index(i, 0); + emit dataChanged(idx, idx, { VaultBalanceRole }); + } + return; + } + } +} + +bool LEZWalletAccountModel::isPublicAccount(const QString& accountId, bool defaultValue) const +{ + for (const LEZWalletAccountEntry& e : m_entries) { + if (e.accountId == accountId) + return e.isPublic; + } + return defaultValue; +} diff --git a/src/LEZWalletAccountModel.h b/src/LEZWalletAccountModel.h index 2ef815f..7afa6e2 100644 --- a/src/LEZWalletAccountModel.h +++ b/src/LEZWalletAccountModel.h @@ -1,17 +1,30 @@ #pragma once #include -#include +#include #include #include +// Public accounts have no key group, so they all share the PublicSectionKey section. +// Private accounts section by NPK (the key group they belong to) — see +// LEZWalletBackend::buildEnrichedAccountList, which attaches "npk"/"keys_json" per entry. +inline const QString PublicSectionKey = QStringLiteral("public"); + struct LEZWalletAccountEntry { QString name; QString accountId; QString balance; + QString vaultBalance; // claimable balance held in this account's bridge vault PDA bool isPublic = true; + QString sectionKey; + QString keysJson; // {nullifier_public_key, viewing_public_key} shared by the whole section; private only + bool isFirstInGroup = false; // QML renders the section header above rows where this is true }; +// Note: this model is exposed to QML via Qt Remote Objects model replication (see +// logos.model() in ExecutionZoneWalletView.qml), which only replicates roles — not +// arbitrary Q_PROPERTYs. Anything QML needs must be a role on the row, not a property +// on the model itself. class LEZWalletAccountModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(int count READ count NOTIFY countChanged) @@ -20,7 +33,11 @@ public: NameRole = Qt::UserRole + 1, AccountIdRole, BalanceRole, - IsPublicRole + VaultBalanceRole, + IsPublicRole, + SectionKeyRole, + KeysJsonRole, + IsFirstInGroupRole }; Q_ENUM(Role) @@ -30,11 +47,17 @@ public: QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QHash roleNames() const override; - void replaceFromJsonArray(const QJsonArray& arr); void replaceFromVariantList(const QVariantList& list); void setBalanceByAccountId(const QString& accountId, const QString& balance); + void setVaultBalanceByAccountId(const QString& accountId, const QString& vaultBalance); int count() const { return m_entries.size(); } + // Authoritative isPublic lookup by account ID — used to validate/derive the flag + // server-side instead of trusting a caller-supplied value, since this model is the + // source of truth for which accounts the wallet actually owns. Falls back to + // `defaultValue` if the account isn't found. + bool isPublicAccount(const QString& accountId, bool defaultValue = true) const; + signals: void countChanged(); diff --git a/src/LEZWalletBackend.cpp b/src/LEZWalletBackend.cpp index afcccb2..703c338 100644 --- a/src/LEZWalletBackend.cpp +++ b/src/LEZWalletBackend.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -30,6 +29,10 @@ namespace { // Timeout(-1) means "wait indefinitely", matching Qt's own convention // for infinite waits (e.g. QRemoteObjectPendingCall::waitForFinished(-1)). const Timeout NO_TIMEOUT{-1}; + // Warm-up retry budget for openIfPathsConfigured(), 50ms x up to 100 attempts + // ~= 5s, matching LogosAPIConsumer's own connect-retry budget. + const int MODULE_WARMUP_RETRY_MS = 50; + const int MODULE_WARMUP_MAX_ATTEMPTS = 100; // Convert a decimal amount string to 32-char hex (16 bytes little-endian) // for transfer_public/transfer_private/transfer_private_owned. @@ -61,12 +64,14 @@ LEZWalletBackend::LEZWalletBackend(LogosAPI* logosAPI, QObject* parent) m_accountModel(new LEZWalletAccountModel(this)), m_filteredAccountModel(new LEZAccountFilterModel(this)), m_privateAccountModel(new LEZAccountFilterModel(this)), + m_claimableAccountModel(new LEZClaimableAccountFilterModel(this)), m_logosAPI(logosAPI ? logosAPI : new LogosAPI("lez_wallet_ui", this)), m_logos(new LogosModules(m_logosAPI)) { m_filteredAccountModel->setSourceModel(m_accountModel); m_privateAccountModel->setFilterByPublic(false); m_privateAccountModel->setSourceModel(m_accountModel); + m_claimableAccountModel->setSourceModel(m_accountModel); // Initialise PROP defaults via the generated setters. setIsWalletOpen(false); @@ -118,29 +123,74 @@ void LEZWalletBackend::persistStoragePath(const QString& path) QSettings(SETTINGS_ORG, SETTINGS_APP).setValue(STORAGE_PATH_KEY, localPath); } -void LEZWalletBackend::openIfPathsConfigured() +void LEZWalletBackend::openIfPathsConfigured(int attempt) { if (configPath().isEmpty() || storagePath().isEmpty()) return; + // The first cross-process call this module makes to logos_execution_zone can race + // the inter-module capability/auth-token handshake: if it goes out before the + // target has been informed of our token, ModuleProxy rejects it and the call + // resolves to a default-constructed return value -- 0 for open()'s int64_t, which + // is indistinguishable from WALLET_FFI_SUCCESS. Warm up with a harmless, + // wallet-state-free call first: version() defaults to "" on that same rejection, + // which IS distinguishable from its real non-empty value, so we can retry here + // until the handshake has settled before trusting open()'s result. Once any call + // to logos_execution_zone succeeds, its token is cached for the rest of the + // session, so open() itself won't hit this race afterwards. + if (m_logos->logos_execution_zone.version().isEmpty() && attempt < MODULE_WARMUP_MAX_ATTEMPTS) { + QTimer::singleShot(MODULE_WARMUP_RETRY_MS, this, + [this, attempt]() { openIfPathsConfigured(attempt + 1); }); + return; + } + qDebug() << "LEZWalletBackend: opening wallet with config" << configPath() << "storage" << storagePath(); int err = m_logos->logos_execution_zone.open(configPath(), storagePath()); if (err == WALLET_FFI_SUCCESS) { qDebug() << "LEZWalletBackend: wallet opened successfully"; - setIsWalletOpen(true); - m_accountModel->replaceFromVariantList(m_logos->logos_execution_zone.list_accounts()); - fetchAndUpdateBlockHeights(); - startChunkedSync(); - refreshSequencerAddr(); + finishOpeningWallet(); } else { qWarning() << "LEZWalletBackend: failed to open wallet, error" << err << "config:" << configPath() << "storage:" << storagePath(); } } +// Tags each private account with the NPK of the key group it belongs to (plus that +// group's {nullifier_public_key, viewing_public_key} JSON), so the model can section +// accounts by key group instead of listing them flat. Public accounts are untouched. +QVariantList LEZWalletBackend::buildEnrichedAccountList() +{ + QVariantList raw = m_logos->logos_execution_zone.list_accounts(); + QVariantList enriched; + enriched.reserve(raw.size()); + for (const QVariant& v : raw) { + QVariantMap map = v.toMap(); + if (!map.value(QStringLiteral("is_public"), true).toBool()) { + const QString accountId = map.value(QStringLiteral("account_id")).toString(); + const QString keysJson = getPrivateAccountKeys(accountId); + const QJsonDocument doc = QJsonDocument::fromJson(keysJson.toUtf8()); + if (doc.isObject()) { + map[QStringLiteral("npk")] = doc.object().value(QStringLiteral("nullifier_public_key")).toString(); + map[QStringLiteral("keys_json")] = keysJson; + } + } + enriched.append(map); + } + return enriched; +} + +void LEZWalletBackend::finishOpeningWallet() +{ + setIsWalletOpen(true); + m_accountModel->replaceFromVariantList(buildEnrichedAccountList()); + fetchAndUpdateBlockHeights(); + startChunkedSync(); + refreshSequencerAddr(); +} + void LEZWalletBackend::refreshAccounts() { - m_accountModel->replaceFromVariantList(m_logos->logos_execution_zone.list_accounts()); + m_accountModel->replaceFromVariantList(buildEnrichedAccountList()); fetchAndUpdateBlockHeights(); if (!m_syncing) startChunkedSync(); @@ -169,6 +219,9 @@ void LEZWalletBackend::syncNextChunk() const quint64 synced = static_cast(lastSyncedBlock()); if (synced >= m_syncTarget) { m_syncing = false; + // Sync may have discovered new private accounts (e.g. shielded transfers to a + // foreign NPK/VPK); re-list so the model picks them up without a restart. + m_accountModel->replaceFromVariantList(buildEnrichedAccountList()); fetchAndUpdateBlockHeights(); updateBalances(); return; @@ -333,6 +386,50 @@ QString LEZWalletBackend::transferDeshielded(QString fromHex, QString toHex, QSt NO_TIMEOUT).toString(); } +QString LEZWalletBackend::bridgeWithdraw(QString fromHex, QString bedrockAccountPkHex, quint64 amount) +{ + return m_logos->logos_execution_zone.bridge_withdraw(fromHex, bedrockAccountPkHex, amount); +} + +QString LEZWalletBackend::getVaultBalance(const QString& accountIdHex) +{ + return m_logos->logos_execution_zone.get_vault_balance(accountIdHex); +} + +void LEZWalletBackend::refreshVaultBalances() +{ + if (!m_accountModel) return; + for (int i = 0; i < m_accountModel->count(); ++i) { + const QModelIndex idx = m_accountModel->index(i, 0); + const QString addr = m_accountModel->data(idx, LEZWalletAccountModel::AccountIdRole).toString(); + const QString vaultBal = getVaultBalance(addr); + if (!vaultBal.isEmpty()) + m_accountModel->setVaultBalanceByAccountId(addr, vaultBal); + } +} + +QString LEZWalletBackend::vaultClaim(QString fromHex, bool isPublic, QString amountStr) +{ + const QString amountHex = amountToLe16Hex(amountStr); + if (amountHex.isEmpty()) return QStringLiteral("Error: Invalid amount."); + // Don't trust the caller-supplied isPublic — the account model is the source of + // truth for which accounts the wallet owns and whether each is public or private. + const bool actuallyPublic = m_accountModel + ? m_accountModel->isPublicAccount(fromHex, isPublic) + : isPublic; + if (actuallyPublic) + return m_logos->logos_execution_zone.vault_claim(fromHex, amountHex); + + // vault_claim_private generates a proof, like transfer_private/transfer_shielded + // above — go through invokeRemoteMethod with NO_TIMEOUT instead of the generated + // accessor, which applies the SDK's default 20s Timeout and returns before the + // proof is actually done and the tx submitted. + return m_logosAPI->getClient(LEZ_MODULE)->invokeRemoteMethod( + LEZ_MODULE, "vault_claim_private", + QVariantList{fromHex.trimmed(), amountHex}, + NO_TIMEOUT).toString(); +} + void LEZWalletBackend::applySequencerAddrToConfig(const QString& configPath, const QString& sequencerAddr) { QJsonObject obj; @@ -354,24 +451,40 @@ void LEZWalletBackend::applySequencerAddrToConfig(const QString& configPath, con file.write(QJsonDocument(obj).toJson(QJsonDocument::Indented)); } -bool LEZWalletBackend::createNew(QString configPath, QString storagePath, QString password, QString sequencerAddr) +QString LEZWalletBackend::createNew(QString configPath, QString storagePath, QString password, QString sequencerAddr) { const QString localConfigPath = toLocalPath(configPath); const QString localStoragePath = toLocalPath(storagePath); + // Both files already on disk: this is most likely an existing wallet the + // user pointed us at (e.g. from the setup screen), not a request to + // overwrite it. Try to load it instead of blindly creating a new one. + if (QFile::exists(localConfigPath) && QFile::exists(localStoragePath)) { + int err = m_logos->logos_execution_zone.open(localConfigPath, localStoragePath); + if (err != WALLET_FFI_SUCCESS) { + return QStringLiteral( + "Could not load the wallet at the selected paths. Pick " + "different existing config/storage files, or choose paths " + "that don't exist yet to create a new wallet there."); + } + persistConfigPath(localConfigPath); + persistStoragePath(localStoragePath); + finishOpeningWallet(); + return QString(); + } + if (!sequencerAddr.isEmpty()) applySequencerAddrToConfig(localConfigPath, sequencerAddr); const QString mnemonic = m_logos->logos_execution_zone.create_new( localConfigPath, localStoragePath, password); - if (mnemonic.isEmpty()) return false; + if (mnemonic.isEmpty()) + return QStringLiteral("Failed to create wallet. Check paths and try again."); persistConfigPath(localConfigPath); persistStoragePath(localStoragePath); - setIsWalletOpen(true); - refreshAccounts(); - refreshSequencerAddr(); - return true; + finishOpeningWallet(); + return QString(); } void LEZWalletBackend::copyToClipboard(QString text) diff --git a/src/LEZWalletBackend.h b/src/LEZWalletBackend.h index 7706d8d..469ee63 100644 --- a/src/LEZWalletBackend.h +++ b/src/LEZWalletBackend.h @@ -7,6 +7,7 @@ #include "rep_LEZWalletBackend_source.h" #include "LEZAccountFilterModel.h" +#include "LEZClaimableAccountFilterModel.h" #include "LEZWalletAccountModel.h" class LogosAPI; @@ -20,6 +21,7 @@ class LEZWalletBackend : public LEZWalletBackendSimpleSource { Q_PROPERTY(LEZWalletAccountModel* accountModel READ accountModel CONSTANT) Q_PROPERTY(LEZAccountFilterModel* filteredAccountModel READ filteredAccountModel CONSTANT) Q_PROPERTY(LEZAccountFilterModel* privateAccountModel READ privateAccountModel CONSTANT) + Q_PROPERTY(LEZClaimableAccountFilterModel* claimableAccountModel READ claimableAccountModel CONSTANT) public: explicit LEZWalletBackend(LogosAPI* logosAPI = nullptr, QObject* parent = nullptr); @@ -28,6 +30,7 @@ public: LEZWalletAccountModel* accountModel() const { return m_accountModel; } LEZAccountFilterModel* filteredAccountModel() const { return m_filteredAccountModel; } LEZAccountFilterModel* privateAccountModel() const { return m_privateAccountModel; } + LEZClaimableAccountFilterModel* claimableAccountModel() const { return m_claimableAccountModel; } public slots: // Overrides of the pure-virtual slots generated from the .rep. @@ -45,7 +48,10 @@ public slots: QString transferShielded(QString fromHex, QString toKeysJson, QString amountStr) override; QString transferShieldedOwned(QString fromHex, QString toHex, QString amountStr) override; QString transferDeshielded(QString fromHex, QString toHex, QString amountStr) override; - bool createNew(QString configPath, QString storagePath, QString password, QString sequencerAddr) override; + QString bridgeWithdraw(QString fromHex, QString bedrockAccountPkHex, quint64 amount) override; + void refreshVaultBalances() override; + QString vaultClaim(QString fromHex, bool isPublic, QString amountStr) override; + QString createNew(QString configPath, QString storagePath, QString password, QString sequencerAddr) override; void copyToClipboard(QString text) override; private slots: @@ -57,11 +63,14 @@ private: void applySequencerAddrToConfig(const QString& configPath, const QString& sequencerAddr); void fetchAndUpdateBlockHeights(); void startChunkedSync(); + QVariantList buildEnrichedAccountList(); void updateBalances(); + QString getVaultBalance(const QString& accountIdHex); void refreshSequencerAddr(); void saveWallet(); - void openIfPathsConfigured(); + void openIfPathsConfigured(int attempt = 0); + void finishOpeningWallet(); bool m_syncing = false; quint64 m_syncTarget = 0; @@ -70,6 +79,7 @@ private: LEZWalletAccountModel* m_accountModel; LEZAccountFilterModel* m_filteredAccountModel; LEZAccountFilterModel* m_privateAccountModel; + LEZClaimableAccountFilterModel* m_claimableAccountModel; LogosAPI* m_logosAPI; LogosModules* m_logos; diff --git a/src/LEZWalletBackend.rep b/src/LEZWalletBackend.rep index 376f3e4..ab56de3 100644 --- a/src/LEZWalletBackend.rep +++ b/src/LEZWalletBackend.rep @@ -23,7 +23,12 @@ class LEZWalletBackend SLOT(QString transferShieldedOwned(QString fromHex, QString toHex, QString amountStr)) SLOT(QString transferDeshielded(QString fromHex, QString toHex, QString amountStr)) - SLOT(bool createNew(QString configPath, QString storagePath, QString password, QString sequencerAddr)) + SLOT(QString bridgeWithdraw(QString fromHex, QString bedrockAccountPkHex, quint64 amount)) + + SLOT(void refreshVaultBalances()) + SLOT(QString vaultClaim(QString fromHex, bool isPublic, QString amountStr)) + + SLOT(QString createNew(QString configPath, QString storagePath, QString password, QString sequencerAddr)) SLOT(void copyToClipboard(QString text)) } diff --git a/src/qml/ExecutionZoneWalletView.qml b/src/qml/ExecutionZoneWalletView.qml index 1b46a66..e6d4fdf 100644 --- a/src/qml/ExecutionZoneWalletView.qml +++ b/src/qml/ExecutionZoneWalletView.qml @@ -13,6 +13,7 @@ Rectangle { readonly property var accountModel: logos.model("lez_wallet_ui", "accountModel") readonly property var publicAccountModel: logos.model("lez_wallet_ui", "filteredAccountModel") readonly property var privateAccountModel: logos.model("lez_wallet_ui", "privateAccountModel") + readonly property var claimableAccountModel: logos.model("lez_wallet_ui", "claimableAccountModel") property bool ready: false Connections { @@ -118,10 +119,13 @@ Rectangle { configPath: backend ? backend.configPath : "" onCreateWallet: function(configPath, storagePath, password, sequencerUrl) { if (!backend) return + // createNew() returns an empty string on success, or a + // human-readable error message (e.g. existing files at + // the chosen paths failed to load) otherwise. logos.watch(backend.createNew(configPath, storagePath, password, sequencerUrl), - function(ok) { - if (!ok) - createError = qsTr("Failed to create wallet. Check paths and try again.") + function(errorMessage) { + if (errorMessage) + createError = errorMessage }, function(error) { createError = qsTr("Error creating wallet: %1").arg(error) @@ -138,6 +142,7 @@ Rectangle { accountModel: root.accountModel publicAccountModel: root.publicAccountModel privateAccountModel: root.privateAccountModel + claimableAccountModel: root.claimableAccountModel lastSyncedBlock: backend ? backend.lastSyncedBlock : 0 currentBlockHeight: backend ? backend.currentBlockHeight : 0 @@ -229,21 +234,49 @@ Rectangle { dashboardView.transferTxHash = "" }) } + onBridgeWithdrawRequested: (fromId, bedrockAccountPkHex, amount) => { + if (!backend) return + var parsedAmount = Number(amount) + if (!Number.isInteger(parsedAmount) || parsedAmount <= 0) { + dashboardView.transferResult = qsTr("Error: Invalid amount.") + dashboardView.transferResultIsError = true + dashboardView.transferTxHash = "" + return + } + logos.watch(backend.bridgeWithdraw(fromId, bedrockAccountPkHex, parsedAmount), + function(raw) { ffiErrors.applyTransferResult(dashboardView, raw) }, + function(error) { + dashboardView.transferResult = qsTr("Error: %1").arg(error) + dashboardView.transferResultIsError = true + dashboardView.transferTxHash = "" + }) + } + onVaultClaimRequested: (fromId, isPublic, amount) => { + if (!backend) return + dashboardView.transferPending = !isPublic + logos.watch(backend.vaultClaim(fromId, isPublic, amount), + function(raw) { + dashboardView.transferPending = false + ffiErrors.applyTransferResult(dashboardView, raw) + backend.refreshVaultBalances() + backend.refreshBalances() + }, + function(error) { + dashboardView.transferPending = false + dashboardView.transferResult = qsTr("Error: %1").arg(error) + dashboardView.transferResultIsError = true + dashboardView.transferTxHash = "" + }) + } + onRefreshClaimableDepositsRequested: { + if (!backend) return + backend.refreshVaultBalances() // void slot, fire-and-forget + } onCopyRequested: (copyText) => { clipHelper.text = copyText clipHelper.selectAll() clipHelper.copy() } - onCopyPublicKeysRequested: (accountIdHex) => { - if (!backend) return - logos.watch(backend.getPrivateAccountKeys(accountIdHex), - function(keys) { - clipHelper.text = keys - clipHelper.selectAll() - clipHelper.copy() - }, - function(error) { console.warn("getPrivateAccountKeys failed:", error) }) - } } } } diff --git a/src/qml/controls/AccountComboBox.qml b/src/qml/controls/AccountComboBox.qml index 8d42c27..5eba3d0 100644 --- a/src/qml/controls/AccountComboBox.qml +++ b/src/qml/controls/AccountComboBox.qml @@ -12,7 +12,6 @@ ComboBox { // Forwarded from AccountDelegate's copy button — bubble up to the parent // view, which calls backend.copyToClipboard(). signal copyRequested(string text) - signal copyPublicKeysRequested(string accountIdHex) leftPadding: 12 rightPadding: 12 @@ -62,7 +61,6 @@ ComboBox { width: root.popup ? (root.popup.width - root.popup.leftPadding - root.popup.rightPadding) : 368 highlighted: root.highlightedIndex === index onCopyRequested: (text) => root.copyRequested(text) - onCopyPublicKeysRequested: (id) => root.copyPublicKeysRequested(id) } popup: Popup { diff --git a/src/qml/controls/AccountDelegate.qml b/src/qml/controls/AccountDelegate.qml index 9c4be1f..77b0e03 100644 --- a/src/qml/controls/AccountDelegate.qml +++ b/src/qml/controls/AccountDelegate.qml @@ -14,7 +14,6 @@ ItemDelegate { // the global QML scope for `backend` since it now lives behind the // logos.module() bridge in the parent view. signal copyRequested(string text) - signal copyPublicKeysRequested(string accountIdHex) leftPadding: Theme.spacing.medium rightPadding: Theme.spacing.medium @@ -78,9 +77,7 @@ ItemDelegate { LogosCopyButton { Layout.preferredHeight: 40 Layout.preferredWidth: 40 - onCopyText: model.isPublic - ? root.copyRequested(Base58.encode(model.accountId ?? "")) - : root.copyPublicKeysRequested(model.accountId ?? "") + onCopyText: root.copyRequested(Base58.encode(model.accountId ?? "")) visible: addressLabel.text icon.color: Theme.palette.textMuted } diff --git a/src/qml/views/AccountsPanel.qml b/src/qml/views/AccountsPanel.qml index aefce03..d52d33d 100644 --- a/src/qml/views/AccountsPanel.qml +++ b/src/qml/views/AccountsPanel.qml @@ -21,7 +21,6 @@ Rectangle { signal createPrivateAccountRequested() signal fetchBalancesRequested() signal copyRequested(string text) - signal copyPublicKeysRequested(string accountIdHex) radius: Theme.spacing.radiusXlarge color: Theme.palette.backgroundSecondary @@ -63,7 +62,7 @@ Rectangle { // Sync progress ColumnLayout { Layout.fillWidth: true - spacing: Theme.spacing.xsmall + spacing: Theme.spacing.small visible: root.currentBlockHeight > 0 && root.lastSyncedBlock < root.currentBlockHeight RowLayout { @@ -127,10 +126,54 @@ Rectangle { spacing: Theme.spacing.small model: root.accountModel - delegate: AccountDelegate { + // Each private account's "keysJson"/"sectionKey"/"isFirstInGroup" are plain + // model roles (replicated like any other row data), so the group header for + // the section a row starts is rendered inline here rather than via + // ListView.section — section.delegate only gets the section's string value, + // with no way back to that row's data once the model is a remote replica. + delegate: ColumnLayout { width: listView.width - onCopyRequested: (text) => root.copyRequested(text) - onCopyPublicKeysRequested: (id) => root.copyPublicKeysRequested(id) + spacing: Theme.spacing.small + + // keysJson is only ever read inside the copy button's click handler below, + // never bound to anything visible — so unlike accountId/isPublic/isFirstInGroup + // (which are warmed up by their visible bindings), the Qt Remote Objects model + // replica never requests this role from the source process until something + // actually reads it. Without this binding, the first click reads a not-yet- + // fetched (empty) value and the real data only arrives in time for the next + // click. Referencing it here forces the role to be requested as soon as the + // row is created. + property string keysJsonWarm: model.keysJson ?? "" + + RowLayout { + Layout.fillWidth: true + visible: model.isFirstInGroup ?? false + spacing: Theme.spacing.small + + LogosText { + text: model.isPublic + ? qsTr("Public Accounts") + : qsTr("Private") + font.pixelSize: Theme.typography.secondaryText + font.bold: true + color: Theme.palette.textSecondary + } + + Item { Layout.fillWidth: true } + + LogosCopyButton { + Layout.preferredHeight: 32 + Layout.preferredWidth: 32 + visible: !model.isPublic + icon.color: Theme.palette.textMuted + onCopyText: root.copyRequested(model.keysJson ?? "") + } + } + + AccountDelegate { + Layout.fillWidth: true + onCopyRequested: (text) => root.copyRequested(text) + } } } diff --git a/src/qml/views/BridgePanel.qml b/src/qml/views/BridgePanel.qml new file mode 100644 index 0000000..0e41d9e --- /dev/null +++ b/src/qml/views/BridgePanel.qml @@ -0,0 +1,69 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Logos.Theme +import Logos.Controls + +Item { + id: root + + // --- Public API: data in --- + property var publicAccountModel: null + property var claimableAccountModel: null + property bool transferPending: false + + // --- Public API: signals out --- + signal bridgeWithdrawRequested(string fromAccountId, string bedrockAccountPkHex, string amount) + signal vaultClaimRequested(string fromAccountId, bool isPublic, string amount) + signal refreshClaimableDepositsRequested() + signal copyRequested(string copyText) + + ColumnLayout { + anchors.fill: parent + spacing: Theme.spacing.large + + // Bridge section toggle + TabBar { + id: bridgeSectionBar + Layout.fillWidth: true + spacing: Theme.spacing.small + currentIndex: 0 + + background: Rectangle { + color: Theme.palette.backgroundSecondary + radius: Theme.spacing.radiusSmall + } + + LogosTabButton { + text: qsTr("Withdraw") + } + + LogosTabButton { + text: qsTr("Claim Deposit") + } + } + + StackLayout { + Layout.fillWidth: true + Layout.fillHeight: true + currentIndex: bridgeSectionBar.currentIndex + + WithdrawPanel { + publicAccountModel: root.publicAccountModel + transferPending: root.transferPending + + onBridgeWithdrawRequested: (fromId, bedrockAccountPkHex, amount) => root.bridgeWithdrawRequested(fromId, bedrockAccountPkHex, amount) + onCopyRequested: (copyText) => root.copyRequested(copyText) + } + + ClaimDepositPanel { + claimableAccountModel: root.claimableAccountModel + claimPending: root.transferPending + + onVaultClaimRequested: (fromId, isPublic, amount) => root.vaultClaimRequested(fromId, isPublic, amount) + onRefreshRequested: root.refreshClaimableDepositsRequested() + } + } + } +} diff --git a/src/qml/views/ClaimDepositPanel.qml b/src/qml/views/ClaimDepositPanel.qml new file mode 100644 index 0000000..5f13d12 --- /dev/null +++ b/src/qml/views/ClaimDepositPanel.qml @@ -0,0 +1,98 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Logos.Theme +import Logos.Controls + +import "../Base58.js" as Base58 + +Item { + id: root + + // --- Public API: data in --- + property var claimableAccountModel: null + property bool claimPending: false + + // --- Public API: signals out --- + signal vaultClaimRequested(string fromAccountId, bool isPublic, string amount) + signal refreshRequested() + + onVisibleChanged: if (visible) root.refreshRequested() + + ColumnLayout { + anchors.fill: parent + spacing: Theme.spacing.large + + LogosText { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.topMargin: Theme.spacing.xlarge + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + text: qsTr("No deposits pending claim") + font.pixelSize: Theme.typography.secondaryText + color: Theme.palette.textSecondary + visible: !listView.visible + } + + ListView { + id: listView + Layout.fillWidth: true + Layout.fillHeight: true + visible: count > 0 + clip: true + spacing: Theme.spacing.small + model: root.claimableAccountModel + + delegate: ItemDelegate { + id: delegateRoot + width: listView.width + + leftPadding: Theme.spacing.medium + rightPadding: Theme.spacing.medium + topPadding: Theme.spacing.medium + bottomPadding: Theme.spacing.medium + + background: Rectangle { + color: delegateRoot.hovered ? Theme.palette.backgroundMuted : Theme.palette.backgroundTertiary + radius: Theme.spacing.radiusLarge + } + + contentItem: RowLayout { + spacing: Theme.spacing.medium + + ColumnLayout { + Layout.fillWidth: true + spacing: Theme.spacing.tiny + + LogosText { + text: model.name || (qsTr("Account ") + Base58.encode(model.accountId ?? "").slice(0, 4)) + font.pixelSize: Theme.typography.secondaryText + font.bold: true + } + + LogosText { + text: qsTr("Claimable: %1").arg(model.vaultBalance ?? "0") + font.pixelSize: Theme.typography.secondaryText + color: Theme.palette.textSecondary + } + } + + LogosButton { + text: qsTr("Claim") + enabled: !root.claimPending + onClicked: root.vaultClaimRequested(model.accountId, model.isPublic, model.vaultBalance) + } + } + } + } + + LogosButton { + Layout.fillWidth: true + text: qsTr("Refresh") + enabled: !root.claimPending + onClicked: root.refreshRequested() + } + } +} diff --git a/src/qml/views/DashboardView.qml b/src/qml/views/DashboardView.qml index d412515..0076765 100644 --- a/src/qml/views/DashboardView.qml +++ b/src/qml/views/DashboardView.qml @@ -12,6 +12,7 @@ Rectangle { property var accountModel: null property var publicAccountModel: null property var privateAccountModel: null + property var claimableAccountModel: null property string transferResult: "" property string transferTxHash: "" property bool transferResultIsError: false @@ -29,8 +30,10 @@ Rectangle { signal transferShieldedRequested(string fromAccountId, string toKeysJsonOrAddress, string amount) signal transferShieldedOwnedRequested(string fromAccountId, string toAccountId, string amount) signal transferDeshieldedRequested(string fromAccountId, string toAccountId, string amount) + signal bridgeWithdrawRequested(string fromAccountId, string bedrockAccountPkHex, string amount) + signal vaultClaimRequested(string fromAccountId, bool isPublic, string amount) + signal refreshClaimableDepositsRequested() signal copyRequested(string copyText) - signal copyPublicKeysRequested(string accountIdHex) color: Theme.palette.background @@ -52,7 +55,6 @@ Rectangle { onCreatePrivateAccountRequested: root.createPrivateAccountRequested() onFetchBalancesRequested: root.fetchBalancesRequested() onCopyRequested: (text) => root.copyRequested(text) - onCopyPublicKeysRequested: (id) => root.copyPublicKeysRequested(id) } TransferPanel { @@ -61,6 +63,7 @@ Rectangle { Layout.fillHeight: true publicAccountModel: root.publicAccountModel privateAccountModel: root.privateAccountModel + claimableAccountModel: root.claimableAccountModel transferResult: root.transferResult transferTxHash: root.transferTxHash transferResultIsError: root.transferResultIsError @@ -72,6 +75,9 @@ Rectangle { onTransferShieldedRequested: (fromId, toKeysJsonOrAddress, amount) => root.transferShieldedRequested(fromId, toKeysJsonOrAddress, amount) onTransferShieldedOwnedRequested: (fromId, toAccountId, amount) => root.transferShieldedOwnedRequested(fromId, toAccountId, amount) onTransferDeshieldedRequested: (fromId, toAccountId, amount) => root.transferDeshieldedRequested(fromId, toAccountId, amount) + onBridgeWithdrawRequested: (fromId, bedrockAccountPkHex, amount) => root.bridgeWithdrawRequested(fromId, bedrockAccountPkHex, amount) + onVaultClaimRequested: (fromId, isPublic, amount) => root.vaultClaimRequested(fromId, isPublic, amount) + onRefreshClaimableDepositsRequested: root.refreshClaimableDepositsRequested() onCopyRequested: (copyText) => root.copyRequested(copyText) } } diff --git a/src/qml/views/TransferPanel.qml b/src/qml/views/TransferPanel.qml index 73d2f98..6239127 100644 --- a/src/qml/views/TransferPanel.qml +++ b/src/qml/views/TransferPanel.qml @@ -6,7 +6,6 @@ import Logos.Theme import Logos.Controls import "../controls" -import "../Base58.js" as Base58 Rectangle { id: root @@ -14,42 +13,24 @@ Rectangle { // --- Public API: data in --- property var publicAccountModel: null property var privateAccountModel: null + property var claimableAccountModel: null property string transferResult: "" property string transferTxHash: "" property bool transferResultIsError: false property bool transferPending: false - // --- Public API: signals out (match backend: transfer_public, transfer_private, transfer_private_owned, transfer_shielded, transfer_shielded_owned) --- + // --- Public API: signals out (match backend: transfer_public, transfer_private, transfer_private_owned, transfer_shielded, transfer_shielded_owned, transfer_deshielded, bridge_withdraw, vault_claim) --- signal transferPublicRequested(string fromAccountId, string toAddress, string amount) signal transferPrivateRequested(string fromAccountId, string toKeysJsonOrAddress, string amount) signal transferPrivateOwnedRequested(string fromAccountId, string toAccountId, string amount) signal transferShieldedRequested(string fromAccountId, string toKeysJsonOrAddress, string amount) signal transferShieldedOwnedRequested(string fromAccountId, string toAccountId, string amount) signal transferDeshieldedRequested(string fromAccountId, string toAccountId, string amount) + signal bridgeWithdrawRequested(string fromAccountId, string bedrockAccountPkHex, string amount) + signal vaultClaimRequested(string fromAccountId, bool isPublic, string amount) + signal refreshClaimableDepositsRequested() signal copyRequested(string copyText) - readonly property int fromFilterCount: fromCombo.count - readonly property int toFilterCount: toCombo.count - - QtObject { - id: d - property bool useOwnedAccountForTo: false - readonly property bool isPublicTab: transferTypeBar.currentIndex === 0 - readonly property bool isPrivateTab: transferTypeBar.currentIndex === 1 - readonly property bool isShieldedTab: transferTypeBar.currentIndex === 2 - readonly property bool isDeshieldedTab: transferTypeBar.currentIndex === 3 - readonly property bool showOwnedOption: true - readonly property bool toAddressValid: useOwnedAccountForTo - ? (toFilterCount > 0 && toCombo.currentIndex >= 0) - : (toField && toField.text.trim().length > 0) - readonly property bool needsProof: isPrivateTab || isShieldedTab || isDeshieldedTab - readonly property bool sendEnabled: !root.transferPending - && amountField && manualFromField - && amountField.text.length > 0 && d.toAddressValid - && ((fromFilterCount > 0 && fromCombo.currentIndex >= 0) - || (fromFilterCount === 0 && manualFromField.text.trim().length > 0)) - } - radius: Theme.spacing.radiusXlarge color: Theme.palette.backgroundSecondary @@ -58,17 +39,11 @@ Rectangle { anchors.margins: Theme.spacing.large spacing: Theme.spacing.large - LogosText { - text: qsTr("Transfer") - font.pixelSize: Theme.typography.titleText - font.weight: Theme.typography.weightBold - color: Theme.palette.text - } - - // Transfer type toggle + // Main section toggle TabBar { - id: transferTypeBar - Layout.preferredWidth: 400 + id: mainSectionBar + Layout.fillWidth: true + spacing: Theme.spacing.small currentIndex: 0 background: Rectangle { @@ -77,134 +52,42 @@ Rectangle { } LogosTabButton { - text: qsTr("Public") + text: qsTr("Transfer") } LogosTabButton { - text: qsTr("Private") - } - - LogosTabButton { - text: qsTr("Shielded") - } - - LogosTabButton { - text: qsTr("Deshielded") + text: qsTr("Bridge") } } - // From: dropdown when accounts exist, or manual entry when list is empty - ColumnLayout { + StackLayout { Layout.fillWidth: true - spacing: Theme.spacing.small + Layout.fillHeight: true + currentIndex: mainSectionBar.currentIndex - LogosText { - text: qsTr("From") - font.pixelSize: Theme.typography.secondaryText - color: Theme.palette.textSecondary + TransferTypesPanel { + publicAccountModel: root.publicAccountModel + privateAccountModel: root.privateAccountModel + transferPending: root.transferPending + + onTransferPublicRequested: (fromId, toAddress, amount) => root.transferPublicRequested(fromId, toAddress, amount) + onTransferPrivateRequested: (fromId, toKeysJsonOrAddress, amount) => root.transferPrivateRequested(fromId, toKeysJsonOrAddress, amount) + onTransferPrivateOwnedRequested: (fromId, toAccountId, amount) => root.transferPrivateOwnedRequested(fromId, toAccountId, amount) + onTransferShieldedRequested: (fromId, toKeysJsonOrAddress, amount) => root.transferShieldedRequested(fromId, toKeysJsonOrAddress, amount) + onTransferShieldedOwnedRequested: (fromId, toAccountId, amount) => root.transferShieldedOwnedRequested(fromId, toAccountId, amount) + onTransferDeshieldedRequested: (fromId, toAccountId, amount) => root.transferDeshieldedRequested(fromId, toAccountId, amount) + onCopyRequested: (copyText) => root.copyRequested(copyText) } - LogosTextField { - id: manualFromField - Layout.fillWidth: true - placeholderText: qsTr("Paste or type from account ID") - visible: fromFilterCount === 0 - } + BridgePanel { + publicAccountModel: root.publicAccountModel + claimableAccountModel: root.claimableAccountModel + transferPending: root.transferPending - AccountComboBox { - id: fromCombo - Layout.fillWidth: true - model: (d.isPrivateTab || d.isDeshieldedTab) ? root.privateAccountModel : root.publicAccountModel - visible: fromFilterCount > 0 - onCopyRequested: (text) => root.copyRequested(text) - } - } - - // To field - ColumnLayout { - Layout.fillWidth: true - spacing: Theme.spacing.small - - LogosText { - text: qsTr("To") - font.pixelSize: Theme.typography.secondaryText - color: Theme.palette.textSecondary - } - - LogosCheckbox { - id: useOwnedToCheck - visible: d.showOwnedOption - checked: d.useOwnedAccountForTo - onCheckedChanged: d.useOwnedAccountForTo = checked - text: qsTr("Use owned account") - } - - LogosTextField { - id: toField - Layout.fillWidth: true - placeholderText: (d.isPublicTab || d.isDeshieldedTab) ? qsTr("Recipient account ID") : qsTr("Recipient public keys (JSON)") - visible: !d.useOwnedAccountForTo - } - - AccountComboBox { - id: toCombo - Layout.fillWidth: true - model: (d.isPublicTab || d.isDeshieldedTab) ? root.publicAccountModel : root.privateAccountModel - visible: d.useOwnedAccountForTo && toFilterCount > 0 - onCopyRequested: (text) => root.copyRequested(text) - } - } - - // Amount field - ColumnLayout { - Layout.fillWidth: true - spacing: Theme.spacing.small - - LogosText { - text: qsTr("Amount") - font.pixelSize: Theme.typography.secondaryText - color: Theme.palette.textSecondary - } - - LogosTextField { - id: amountField - Layout.fillWidth: true - placeholderText: "0.00" - } - } - - // Send button - LogosButton { - Layout.fillWidth: true - text: qsTr("Send") - font.pixelSize: Theme.typography.secondaryText - enabled: d.sendEnabled - onClicked: { - var fromId = fromFilterCount > 0 && fromCombo.currentIndex >= 0 - ? (fromCombo.currentValue ?? "") - : Base58.decode(manualFromField.text.trim()) - var rawTo = toField.text.trim() - var toAddress = (d.useOwnedAccountForTo && toCombo.currentIndex >= 0) - ? (toCombo.currentValue ?? "") - : (rawTo.startsWith("{") ? rawTo : Base58.decode(rawTo)) - var amount = amountField.text.trim() - if (fromId.length > 0 && toAddress.length > 0 && amount.length > 0) { - if (d.isPublicTab) - root.transferPublicRequested(fromId, toAddress, amount) - else if (d.isPrivateTab) { - if (d.useOwnedAccountForTo) - root.transferPrivateOwnedRequested(fromId, toAddress, amount) - else - root.transferPrivateRequested(fromId, toAddress, amount) - } else if (d.isShieldedTab) { - if (d.useOwnedAccountForTo) - root.transferShieldedOwnedRequested(fromId, toAddress, amount) - else - root.transferShieldedRequested(fromId, toAddress, amount) - } else if (d.isDeshieldedTab) { - root.transferDeshieldedRequested(fromId, toAddress, amount) - } - } + onBridgeWithdrawRequested: (fromId, bedrockAccountPkHex, amount) => root.bridgeWithdrawRequested(fromId, bedrockAccountPkHex, amount) + onVaultClaimRequested: (fromId, isPublic, amount) => root.vaultClaimRequested(fromId, isPublic, amount) + onRefreshClaimableDepositsRequested: root.refreshClaimableDepositsRequested() + onCopyRequested: (copyText) => root.copyRequested(copyText) } } @@ -250,9 +133,5 @@ Rectangle { visible: resultText.text } } - - Item { - Layout.fillHeight: true - } } } diff --git a/src/qml/views/TransferTypesPanel.qml b/src/qml/views/TransferTypesPanel.qml new file mode 100644 index 0000000..8544553 --- /dev/null +++ b/src/qml/views/TransferTypesPanel.qml @@ -0,0 +1,229 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Logos.Theme +import Logos.Controls + +import "../controls" +import "../Base58.js" as Base58 + +Item { + id: root + + // --- Public API: data in --- + property var publicAccountModel: null + property var privateAccountModel: null + property bool transferPending: false + + // --- Public API: signals out (match backend: transfer_public, transfer_private, transfer_private_owned, transfer_shielded, transfer_shielded_owned, transfer_deshielded) --- + signal transferPublicRequested(string fromAccountId, string toAddress, string amount) + signal transferPrivateRequested(string fromAccountId, string toKeysJsonOrAddress, string amount) + signal transferPrivateOwnedRequested(string fromAccountId, string toAccountId, string amount) + signal transferShieldedRequested(string fromAccountId, string toKeysJsonOrAddress, string amount) + signal transferShieldedOwnedRequested(string fromAccountId, string toAccountId, string amount) + signal transferDeshieldedRequested(string fromAccountId, string toAccountId, string amount) + signal copyRequested(string copyText) + + readonly property int fromFilterCount: fromCombo.count + readonly property int toFilterCount: toCombo.count + + QtObject { + id: d + property bool useOwnedAccountForTo: false + readonly property bool isPublicTab: transferTypeBar.currentIndex === 0 + readonly property bool isPrivateTab: transferTypeBar.currentIndex === 1 + readonly property bool isShieldedTab: transferTypeBar.currentIndex === 2 + readonly property bool isDeshieldedTab: transferTypeBar.currentIndex === 3 + readonly property bool needsKeysJson: !useOwnedAccountForTo && (isPrivateTab || isShieldedTab) + readonly property bool toAddressValid: useOwnedAccountForTo + ? (root.toFilterCount > 0 && toCombo.currentIndex >= 0) + : (needsKeysJson + ? d.isValidKeysJson(toField && toField.text) + : (toField && toField.text.trim().length > 0)) + readonly property bool needsProof: isPrivateTab || isShieldedTab || isDeshieldedTab + readonly property bool sendEnabled: !root.transferPending + && amountField && manualFromField + && amountField.text.length > 0 && d.toAddressValid + && ((root.fromFilterCount > 0 && fromCombo.currentIndex >= 0) + || (root.fromFilterCount === 0 && manualFromField.text.trim().length > 0)) + + // Private/shielded transfers (when not sending to an owned account) require the + // recipient's {nullifier_public_key, viewing_public_key} JSON — the same JSON the + // section-header copy button produces — not a bare account ID. + function isValidKeysJson(text) { + var trimmed = (text || "").trim() + if (trimmed.length === 0) return false + var obj + try { + obj = JSON.parse(trimmed) + } catch (e) { + return false + } + return !!obj && typeof obj === "object" && !Array.isArray(obj) + && typeof obj.nullifier_public_key === "string" && obj.nullifier_public_key.length > 0 + && typeof obj.viewing_public_key === "string" && obj.viewing_public_key.length > 0 + } + } + + ColumnLayout { + anchors.fill: parent + spacing: Theme.spacing.large + + // Transfer type toggle + TabBar { + id: transferTypeBar + Layout.fillWidth: true + spacing: Theme.spacing.small + currentIndex: 0 + + background: Rectangle { + color: Theme.palette.backgroundSecondary + radius: Theme.spacing.radiusSmall + } + + LogosTabButton { + text: qsTr("Public") + } + + LogosTabButton { + text: qsTr("Private") + } + + LogosTabButton { + text: qsTr("Shielded") + } + + LogosTabButton { + text: qsTr("Deshielded") + } + } + + // From: dropdown when accounts exist, or manual entry when list is empty + ColumnLayout { + Layout.fillWidth: true + spacing: Theme.spacing.small + + LogosText { + text: qsTr("From") + font.pixelSize: Theme.typography.secondaryText + color: Theme.palette.textSecondary + } + + LogosTextField { + id: manualFromField + Layout.fillWidth: true + placeholderText: qsTr("Paste or type from account ID") + visible: root.fromFilterCount === 0 + } + + AccountComboBox { + id: fromCombo + Layout.fillWidth: true + model: (d.isPrivateTab || d.isDeshieldedTab) ? root.privateAccountModel : root.publicAccountModel + visible: root.fromFilterCount > 0 + onCopyRequested: (text) => root.copyRequested(text) + } + } + + // To field + ColumnLayout { + Layout.fillWidth: true + spacing: Theme.spacing.small + + LogosText { + text: qsTr("To") + font.pixelSize: Theme.typography.secondaryText + color: Theme.palette.textSecondary + } + + LogosCheckbox { + id: useOwnedToCheck + checked: d.useOwnedAccountForTo + onCheckedChanged: d.useOwnedAccountForTo = checked + text: qsTr("Use owned account") + } + + LogosTextField { + id: toField + Layout.fillWidth: true + placeholderText: (d.isPublicTab || d.isDeshieldedTab) ? qsTr("Recipient account ID") : qsTr("Recipient public keys (JSON)") + visible: !d.useOwnedAccountForTo + } + + LogosText { + Layout.fillWidth: true + visible: d.needsKeysJson && toField.text.trim().length > 0 && !d.toAddressValid + text: qsTr("Enter the recipient's public keys as JSON with nullifier_public_key and viewing_public_key.") + font.pixelSize: Theme.typography.secondaryText + color: Theme.palette.error + wrapMode: Text.WordWrap + } + + AccountComboBox { + id: toCombo + Layout.fillWidth: true + model: (d.isPublicTab || d.isDeshieldedTab) ? root.publicAccountModel : root.privateAccountModel + visible: d.useOwnedAccountForTo && root.toFilterCount > 0 + onCopyRequested: (text) => root.copyRequested(text) + } + } + + // Amount field + ColumnLayout { + Layout.fillWidth: true + spacing: Theme.spacing.small + + LogosText { + text: qsTr("Amount") + font.pixelSize: Theme.typography.secondaryText + color: Theme.palette.textSecondary + } + + LogosTextField { + id: amountField + Layout.fillWidth: true + placeholderText: "0.00" + } + } + + // Send button + LogosButton { + Layout.fillWidth: true + text: qsTr("Send") + font.pixelSize: Theme.typography.secondaryText + enabled: d.sendEnabled + onClicked: { + var fromId = root.fromFilterCount > 0 && fromCombo.currentIndex >= 0 + ? (fromCombo.currentValue ?? "") + : Base58.decode(manualFromField.text.trim()) + var rawTo = toField.text.trim() + var toAddress = (d.useOwnedAccountForTo && toCombo.currentIndex >= 0) + ? (toCombo.currentValue ?? "") + : (d.needsKeysJson ? rawTo : Base58.decode(rawTo)) + var amount = amountField.text.trim() + if (fromId.length > 0 && toAddress.length > 0 && amount.length > 0) { + if (d.isPublicTab) + root.transferPublicRequested(fromId, toAddress, amount) + else if (d.isPrivateTab) { + if (d.useOwnedAccountForTo) + root.transferPrivateOwnedRequested(fromId, toAddress, amount) + else + root.transferPrivateRequested(fromId, toAddress, amount) + } else if (d.isShieldedTab) { + if (d.useOwnedAccountForTo) + root.transferShieldedOwnedRequested(fromId, toAddress, amount) + else + root.transferShieldedRequested(fromId, toAddress, amount) + } else if (d.isDeshieldedTab) { + root.transferDeshieldedRequested(fromId, toAddress, amount) + } + } + } + } + + Item { + Layout.fillHeight: true + } + } +} diff --git a/src/qml/views/WithdrawPanel.qml b/src/qml/views/WithdrawPanel.qml new file mode 100644 index 0000000..52ff706 --- /dev/null +++ b/src/qml/views/WithdrawPanel.qml @@ -0,0 +1,122 @@ +import QtQuick +import QtQuick.Layouts + +import Logos.Theme +import Logos.Controls + +import "../controls" +import "../Base58.js" as Base58 + +Item { + id: root + + // --- Public API: data in --- + property var publicAccountModel: null + property bool transferPending: false + + // --- Public API: signals out (match backend: bridge_withdraw) --- + signal bridgeWithdrawRequested(string fromAccountId, string bedrockAccountPkHex, string amount) + signal copyRequested(string copyText) + + readonly property int fromFilterCount: fromCombo.count + + QtObject { + id: d + readonly property bool sendEnabled: !root.transferPending + && amountField && manualFromField && toField + && amountField.text.length > 0 + && toField.text.trim().length > 0 + && ((root.fromFilterCount > 0 && fromCombo.currentIndex >= 0) + || (root.fromFilterCount === 0 && manualFromField.text.trim().length > 0)) + } + + ColumnLayout { + anchors.fill: parent + spacing: Theme.spacing.large + + // From: dropdown when public accounts exist, or manual entry when list is empty. + // Bridge withdrawals only support public sender accounts. + ColumnLayout { + Layout.fillWidth: true + spacing: Theme.spacing.small + + LogosText { + text: qsTr("From") + font.pixelSize: Theme.typography.secondaryText + color: Theme.palette.textSecondary + } + + LogosTextField { + id: manualFromField + Layout.fillWidth: true + placeholderText: qsTr("Paste or type from account ID") + visible: root.fromFilterCount === 0 + } + + AccountComboBox { + id: fromCombo + Layout.fillWidth: true + model: root.publicAccountModel + visible: root.fromFilterCount > 0 + onCopyRequested: (text) => root.copyRequested(text) + } + } + + // Bedrock (L1) recipient public key + ColumnLayout { + Layout.fillWidth: true + spacing: Theme.spacing.small + + LogosText { + text: qsTr("Bedrock (L1) public key") + font.pixelSize: Theme.typography.secondaryText + color: Theme.palette.textSecondary + } + + LogosTextField { + id: toField + Layout.fillWidth: true + placeholderText: qsTr("Recipient's Bedrock public key (hex)") + } + } + + // Amount field + ColumnLayout { + Layout.fillWidth: true + spacing: Theme.spacing.small + + LogosText { + text: qsTr("Amount") + font.pixelSize: Theme.typography.secondaryText + color: Theme.palette.textSecondary + } + + LogosTextField { + id: amountField + Layout.fillWidth: true + placeholderText: "0.00" + } + } + + // Withdraw button + LogosButton { + Layout.fillWidth: true + text: qsTr("Withdraw") + font.pixelSize: Theme.typography.secondaryText + enabled: d.sendEnabled + onClicked: { + var fromId = root.fromFilterCount > 0 && fromCombo.currentIndex >= 0 + ? (fromCombo.currentValue ?? "") + : Base58.decode(manualFromField.text.trim()) + var toAddress = toField.text.trim() + var amount = amountField.text.trim() + if (fromId.length > 0 && toAddress.length > 0 && amount.length > 0) + root.bridgeWithdrawRequested(fromId, toAddress, amount) + } + } + + Item { + Layout.fillHeight: true + } + } +}