Skip to content

Implement reusable wallet access and QML modules for LEZ program UIs #227

Description

@3esmit

Summary

Implement two reusable targets under apps/shared/wallet/: a headless C++ UI Wallet Access Module and an importable Logos.Wallet QML module.

AMM is the first consumer. Token UI and future LEZ program UIs must use the same targets instead of implementing wallet access and interaction UI independently. Wallet CLI remains an independent Rust consumer of WalletCore; Qt modules, deterministic clients, and guest programs must not become wallet owners.

Problem

The repository has no program-neutral Qt module for wallet sessions, account reads, snapshot caching, durable account creation, generic transaction dispatch, response validation, account presentation, wallet controls, confirmation dialogs, or submitted-transaction presentation.

Without shared modules, each program UI must duplicate generated logos_execution_zone calls, wire validation, cache rules, lifecycle behavior, and QML interaction flows.

Architecture

Arrows point from consumer to dependency.

flowchart LR
    subgraph PROGRAM["PROGRAM UI"]
        PROGVIEW["QML"]
        PROGBACK["backend and runtime"]
    end

    subgraph SHARED["apps/shared/wallet"]
        QML["Logos.Wallet<br/>control, confirmation, result"]
        IFACE["WalletProvider<br/>wallet values and errors"]
        ADAPTER["LogosWalletProvider<br/>session, reads, cache, create, submit"]
        MODEL["WalletAccountModel<br/>stable QML roles"]
        ADAPTER -. "implements" .-> IFACE
        MODEL -->|"uses wallet values"| IFACE
        QML -->|"uses account roles"| MODEL
    end

    subgraph LEZ["Logos Execution Zone"]
        MODULE["logos_execution_zone<br/>generated Qt interface"]
        CORE["WalletCore<br/>keys, construct, sign, submit"]
        MODULE --> CORE
    end

    PROGVIEW -->|"imports"| QML
    PROGVIEW -->|"uses"| PROGBACK
    PROGBACK --> IFACE
    PROGBACK --> MODEL
    ADAPTER --> MODULE
    CLI["Wallet CLI"] -->|"native Rust"| CORE
Loading

Each UI owns one provider adapter, snapshot cache, account model, and WalletControl. Basecamp adapters use the externally shared logos_execution_zone capability; standalone UIs use their own module instance. Do not add global providers, caches, or QML singletons.

Package and ownership

apps/shared/wallet/
├── CMakeLists.txt
├── src/
│   ├── WalletProvider.*
│   ├── LogosWalletProvider.*
│   └── WalletAccountModel.*
└── qml/
    ├── WalletControl.qml
    ├── TransactionConfirmationDialog.qml
    ├── SubmittedTransaction.qml
    ├── AccountDelegate.qml
    ├── CreateAccountDialog.qml
    ├── CreateWalletDialog.qml
    ├── WalletMessageDialog.qml
    └── internal controls and icons

The package produces separately consumable C++/Qt and QML targets. Neither target imports program code.

Ownership remains explicit:

  • Shared access: wallet sessions, validated reads, per-UI snapshots, durable public-account creation, generic dispatch, response validation, and stable program-neutral failures.
  • Shared QML: common wallet interactions, confirmation mechanics, and submitted-transaction presentation.
  • Program UI: QML and Qt Remote Objects interfaces, shell, navigation, readiness, local disconnect preference, account derivation, instruction and signer planning, quote and deadline policy, error mapping, and Base58 result assembly.
  • External wallet: keys, seeds, accounts, approval, transaction construction and signing, sequencer submission, and persistence. Shared access must not own or close this capability.
  • Wallet CLI: direct Rust access to WalletCore, with no Qt dependency.

Deterministic program clients remain pure and wallet-free.

UI Wallet Access requirements

Implement one program-neutral WalletProvider interface, LogosWalletProvider as the only production adapter type, and a fake provider for consumer tests. Compose each production adapter with a non-owning LogosModules access point.

The interface must contain no AMM, Token, QML, or generated wallet SDK types. Only the production adapter may use generated types. Do not add a Bridge, Singleton, provider registry, runtime plugin system, or second facade.

Sessions and accounts

  • Adopt an already-open wallet or open the configured wallet.
  • Create and persist a wallet through the external provider.
  • Validate complete public-account reads. Return the requested account ID and either status: "ok" or status: "read_failed".
  • Require 64-character lowercase hexadecimal account and owner IDs, 16-byte little-endian hexadecimal balance and nonce values, and lowercase even-length hexadecimal data.
  • Return read_failed for malformed, missing, partial, or non-object values.
  • Build one wallet-account snapshot per UI adapter. Reuse it during edits; allow forced refresh before submission; refresh after reconnect or account creation; clear it on application disconnect.
  • Leave mutable program-account refresh policy to each consumer.
  • Create, persist, and read back a public account as one durable operation. Return its state for program-specific default-state and collision checks.
  • Hide low-level save, sync, list, balance, and JSON parsing calls that only implement these operations.

Generic transaction submission

Program clients build program-specific transaction plans. Shared access dispatches them without interpretation. WalletCore turns them into signed LEZ PublicTransaction values.

Accept programId, ordered accountIds[], aligned signingRequirements[], and serialized u32 instruction[]. Preserve every value and call send_generic_public_transaction once.

Accept only a JSON object with boolean success: true, no non-empty error, and a 64-character hexadecimal tx_hash. Return the normalized lowercase Native Wallet Transaction Hash; Base58 conversion belongs to each program result seam.

Logos.Wallet requirements

Register import Logos.Wallet and expose only:

WalletControl {
    wallet: appBackend
    accountModel: appAccountModel
    compact: false
    viewportWidth: width
}

TransactionConfirmationDialog {
    title: qsTr("Confirm transaction")
    busy: flow.submitting
    snapshot: flow.confirmationSnapshot
    summary: programSummaryComponent
    onConfirmed: snapshot => flow.submit(snapshot)
}

SubmittedTransaction {
    title: qsTr("Transaction submitted")
    transactionId: flow.transactionId
}

WalletControl owns connected and disconnected presentation, connect-or-create flow, mandatory seed-phrase backup acknowledgement, active-account selection, public or private account creation, application-local disconnect, wallet errors, busy states, clipboard behavior, and responsive compact layout.

TransactionConfirmationDialog owns the modal overlay, focus, Escape, outside-click handling, snapshot capture, cancel and confirm actions, configurable labels, a non-dismissible busy state, keyboard and accessibility behavior, responsive containment, and a program-summary Component slot. Program UIs retain summary content and the confirmed action. Application confirmation is not external wallet approval.

SubmittedTransaction owns safe wrapping, monospace and Base58-shape presentation, copy action and feedback, and accessible naming. It receives an already assembled ID and must not convert hashes, query status, or claim inclusion or finality.

Keep account delegates, lifecycle dialogs, message dialogs, copy and icon controls, overlays, focus helpers, and icons internal. The injected wallet provides stable state and actions; the model provides name, address, balance, and isPublic.

The QML module must not import or resolve a program module, read or submit program transactions, define program summaries or confirmation policy, own provider state, keys, caches, or network selection, or become a singleton.

Implementation

  1. Add shared C++/Qt and QML targets with contract tests and representative wallet wire fixtures.
  2. Implement provider values, the interface, production and fake adapters, account model, validated reads, per-UI snapshots, durable public-account creation, and generic transaction submission.
  3. Implement Logos.Wallet with the three public types and private supporting QML.
  4. Integrate AMM as the first consumer without adding program knowledge to either shared target.
  5. Keep consumer wiring reversible and avoid parallel implementations or feature flags.
  6. When Token UI is added, consume both targets without copying or forking them.

Compatibility and non-goals

Preserve wallet configuration and storage, generated logos_execution_zone calls and argument order, native account IDs, instruction words, hash bytes, application-local disconnect without closing shared state, and Wallet CLI behavior.

Do not:

  • Share a mutable provider or cache across UIs.
  • Put program navigation, visuals, summaries, or domain rules in either shared module.
  • Treat application confirmation as approval or finality.
  • Move Wallet CLI to Qt.
  • Implement key or seed storage, password security, signing, confirmation auditing, or retry in program UIs.
  • Replace logos_execution_zone, change wallet-native encodings, or give guest programs wallet or network access.
  • Construct transactions in QML.
  • Add runtime network switching or a bulk account RPC.

Acceptance criteria

  • Both targets are independently consumable and contain no program dependency. Token UI can use them without AMM; Wallet CLI remains unchanged.
  • The access interface contains no AMM, Token, QML, or generated wallet SDK types. Only the production adapter parses generated wire values, and production and fake adapters share a contract.
  • Each UI owns its adapter, snapshot cache, account model, and controls. No global mutable wallet state exists.
  • Validated reads, snapshot refresh rules, durable account creation, exact transaction dispatch, native-hash handling, and Base58 ownership match the requirements above.
  • Logos.Wallet exposes only the three public types above and preserves wallet lifecycle, account management, keyboard, accessibility, responsive, confirmation, and submitted-result behavior.
  • AMM consumes both shared targets without direct generated wallet calls, wallet JSON parsing, duplicated generic wallet QML, or shared AMM-specific behavior.

Tests and validation

Shared-module tests cover production and fake adapter contract parity; valid and malformed account payloads; snapshot reuse, refresh, and clearing; durable creation and failure paths; exact account, signer, instruction, and program dispatch; wallet response combinations; and native-hash normalization without Base58 conversion.

QML tests cover disconnected and connected states; wallet creation and seed acknowledgement; account selection and creation; copy and disconnect; model-reset selection clamping; compact layouts; confirmation snapshot and busy behavior; arbitrary program summary content; submitted-ID wrapping and copy feedback; and absence of program-module dependencies.

Validate the shared-module contract tests, QML tests, and at least one AMM consumer build and user flow.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions