Repository navigation
Conversation
The slatepack result types were identical apart from an Epic prefix, so there is now one set. The epicbox/mwcmqs server and config models keep their own classes, Hive type ids and JSON keys but inherit the common fields from RelayServerModel and RelayConfigModel.
EpicTransaction/MwcTransaction and their message types had the same fields. Both native interfaces now return MimblewimbleTransaction; the templates in tool/wl_templates construct it, so regenerate lib/wl_gen after pulling this.
Both wallets carried the same sync percent, scan loop, refresh sequence, balance parsing, slate analysis and wallet info bookkeeping, differing only in how they talk to their native library. The shared parts now live in an abstract MimblewimbleWallet with hooks for the native calls, and the slatepack/relay API (createSlatepack, receiveSlatepack, finalizeSlatepack, getRelayConfig, ...) is declared there so UI can be written against one type. ExtraEpiccashWalletInfo and ExtraMimblewimblecoinWalletInfo had the same JSON shape and are now one class; each wallet still stores it under its own WalletInfoKeys entry.
The finalize view, entry/import/response dialogs, send-slatepack dialog and method toggle existed twice, differing only in the wallet type they cast to and in whether the copy said "slate" or "slatepack". They now take a MimblewimbleWallet and pick their wording from SlatepackTerms. The forked _createSlatepack/_importSlatepack/receive handlers in the send, receive and confirm views collapse the same way. EpicFinalizeView stays as a thin wrapper so its route name keeps working. The transaction method enums and their providers become one enum keyed by coin, keeping Epic's default of relay (epicbox) and MWC's default of slatepack.
The MWC wallet copied Epic Cash's first-block timestamp (1565370278) under the name mimblewimblecoinFirstBlock. MWC mainnet started on 2019-11-11 (1573462801, the value restore_wallet_view already uses), so the estimate for a freshly created wallet landed about 130k blocks above the actual tip: the initial scan and any later rescan from that height never ran. Wallets restored from a seed were unaffected. Staging already fixed this in 0e56467 with 1573462800. Rebased onto it, this only aligns the value with restore_wallet_view.
…ggle and relay config
qr 3 only notices oversized data while painting, after qr_flutter's own validation has passed, so a slatepack too long for a level L code threw during build. QR now checks the data first and shows an errorStateBuilder, defaulting to a short message.
SlatepackInterface is the coin-neutral contract the slatepack screens use: supported methods, on-chain note and invoice capabilities, terms, restore progress, inspectSlatepack, localTxForSlate, create/receive/ finalize, cancel, and unsupported defaults for pending slatepacks, repost, invoices and payment proofs. MimblewimbleWallet mixes it in; inspectSlatepack replaces fullDecodeSlatepack and analyzeSlatepack. SlatepackResult gains state, slateId, amount and fee, FinalizeResult gains posted and kernelExcess, and receive returns a SlatepackResult. UI changes: - Slatepack sends go through prepareSend and the confirm screen on mobile and desktop, so the fee is shown. Epic and MWC prepareSend tag otherData with the method; the confirm screen creates the slatepack. - SlatepackImportFlow inspects any pasted slatepack and dispatches on its state: receive S1, finalize S2, placeholders for invoices, messages for finalized and unknown slates. Finalize summaries use the local tx. - One SlatepackDialog for S1/S2/I1/I2 with copy, share, save to <slateId>.<state>.slatepack, the encrypted flag and a QR code only up to 1000 characters, tap to enlarge. - SlatepackInputField: paste, scan (QrCodeScannerDialog on desktop) and open file with a 256 KiB cap, used wherever a slatepack is entered. - The Finalize nav item and desktop tab become "Slatepack" and open the import flow. Desktop receive always shows the address and QR. - The method toggle is per wallet instead of per coin. - Casts to MimblewimbleWallet and coin gates in slatepack UI use the interface. Relay settings stay coin specific. Dead code removed: Epic's isSlatepack, MWC's slatepack type detection helpers and its slatepack-as-address branch in prepareSend.
The pre-send relay check awaited sink.close() in a finally block. After a failed connection that future never completes, so a send to an unreachable or misconfigured relay left the sending dialog up forever instead of reporting that the relay could not be reached. Move the check into probeEpicboxServer, close the channel without waiting, and bound the connect with a timeout.
Both plugins turn a missing slate message into the string "null", and on a send the first participant entry belongs to the recipient. Taking messages.first therefore showed "null" as the on-chain note of every sent transaction. Use the first participant message that has text.
EpicboxConfig in the plugin reads epicbox_protocol_unsecure, but the model wrote epicbox_protocol_insecure, so the flag never reached it. Write the plugin's key and keep reading the two older spellings. Also make copyWith on the Epicbox and MWCMQS config models apply their arguments.
desktopTextExtraSmall uses the disabled button text color, which is nearly white on the desktop card, so the explanation above the input was almost invisible. Use the subtitle color like the other slatepack hints.
…t/slatepack-interface
Member
Author
|
See staging...refactor/epic-mwc-unify-rebased for an easier-to-review core of this functionality |
sneurlax
marked this pull request as ready for review
November 11, 2026 02:29
Member
Author
|
See that last note:
We will want to update this after those merge |
The send field parses its text as a payment URI as soon as it contains a colon. "http:" parsed as a URI with an empty address, so typing an Epic Cash HTTP listener address cleared the field at the colon and left "//host:port", which failed validation. Only pasting the whole URL worked. Treat http and https URLs as addresses, not payment URIs.
…t/slatepack-interface
The send views added a listener to the address controller inside build() for Epic Cash (and, on desktop, MWC) that reassigned the controller text on every change. Listeners piled up with each rebuild, and resetting the text dropped the selection, so on Android only the first character of a typed address survived; pasting still worked. Strip the relay address prefixes in the field's onChanged instead, and only rewrite the field when that changes the text.
…t/slatepack-interface
Opening Send on an empty MWC wallet asks for a fee estimate. libmwc treats amount == available as send all, retries with the amount minus the required fee and aborts the app on the resulting negative amount (seen on the iOS Simulator as a SIGABRT in mwc_rust_get_tx_fees). There is no fee to estimate without a spendable balance or an amount, so return 0 without calling the native library. The real send still reports insufficient funds.
…t/slatepack-interface
The address field's onChanged strips http(s)://, mailto: and the trailing slash from an Epicbox or MWCMQS address, but onChanged only runs for typed input. Since the build-time controller listener was removed, an address set from a scanned QR code or from a payment URI kept its prefixes. Run the scanned text and the URI address through the same stripping before the address and the field are set, in the mobile send view for Epic Cash and in the desktop send view for Epic Cash and MWC.
Epic Cash and MWC have no default block explorer, and asking for one throws. The block explorer settings listed every coin, so opening the entry for either of them failed while the edit screen was being built, on mobile and in the desktop dialog. Only list coins that have a default block explorer.
…t/slatepack-interface
Creating a slatepack did not compare the result with what the user had just confirmed, reported failures as "Broadcast transaction failed" although nothing is broadcast, and dropped the result without a trace when the confirmation view had closed in the meantime. A slatepack created for another amount is now cancelled and refused with an error naming both amounts. A different fee is logged and shown as a warning only, because the confirmed fee is an estimate and the wallet may select other inputs when it builds the slate. Failures are titled "Could not create the slate" or "slatepack". The sending dialog is closed through a navigator looked up before the send, so it no longer stays up when the view is gone, and that case is logged.
An imported response slatepack was offered for finalizing even when the wallet had no transaction for it, with only a note in the summary, and a transaction the wallet had received counted as a match. The lookup now reports the direction of the local transaction, prefers the send when a wallet holds both sides of a slate, and refreshes the stored history once before giving up, since a send made moments ago may not be in it yet. Without a send of its own the import flow explains that only the wallet that started the transaction can finalize it and no longer shows the Finalize button.
The slatepack dialog refused to draw a QR code for anything over 1000 characters. Epic Cash slates and MWC slatepacks can be longer than that and were drawn at any length before the shared dialog replaced the per-coin ones, so the longer ones lost their QR code. The limit is now what a QR code can hold at the error correction level the widget draws at, 2953 bytes, counted in bytes instead of characters. Longer data still gets the note to copy, share or save it.
The import flow passed whatever was pasted, scanned or opened straight to the wallet, so a slatepack with a greeting, a quote marker or other text around it was rejected. The flow now trims the text and, when it holds an armored slatepack, keeps only the part from the begin marker to the end marker. Both the BEGINSLATEPACK and BEGINSLATE_BIN frames are recognised, with or without the final period. JSON slates and anything without a frame are only trimmed. Inspecting, receiving and finalizing all use that text.
On the slatepack screen a second press of Continue could start the import again while the first was still under way. On a phone the screen closes the keyboard and waits briefly before it starts, which leaves room for that, and the slatepack would then be processed twice. The screen now tracks a running import, ignores further presses and disables the button until the import has returned.
Receiving a slatepack takes a while. If the screen that started the import was gone by the time the wallet had signed it, the response was dropped without a word, even though the wallet had already recorded the transaction and the sender still needs that response. The import flow now keeps hold of the root navigator before it starts and shows the response from there when its own screen has closed. It logs the slate id in that case, and also when a finalize completes after the screen closed. The response text itself is not logged.
The slatepack dialog shared its text without a position for the share sheet. An iPad needs one to place the popover and refuses to open the sheet without it, so Share did nothing there apart from logging an error. The dialog now passes the Share button's rect on screen. Other devices ignore it.
The sending dialog was closed by popping the root navigator. If another route had opened above the dialog by the time the send returned, that route was popped and the dialog stayed up. The dialog's route is now kept and removed directly, so nothing else is closed. A failed HTTP address also closes the dialog when the view is already gone.
The import flow showed its loading overlay through showLoading, which only removes the overlay while the calling screen is still mounted. If that screen closed while the wallet was working, the overlay stayed over the whole app, under the response that is now shown in that case. The flow keeps its own overlay route on the root navigator and removes it when the wallet answers. An error is shown from the root navigator too when the screen is gone.
…/epic-mwc-unify-rebased
…t/slatepack-interface
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Epic Cash and MWC now share one wallet base and one slatepack UI.
Refactor:
MimblewimbleWalletis a shared base forEpicCashWalletandMimblewimblecoinWallet, with a shared native transaction model (MimblewimbleTransaction) and shared slatepack and relay server models.SlatepackInterface, so a coin declares its methods and capabilities instead of the screens checking the coin type.Fixes:
sink.close()that never completes after a failed connection.epicbox_protocol_unsecure), and both older spellings still load.http://orhttps://send address stays in the field. Before, typinghttp:was parsed as a payment URI with an empty address and cleared the field, so only pasting worked.build()that rewrote the field on every change, so on Android only the first typed character survived.Testing:
flutter test, 965 passed, 8 skipped. One Firo cache test from staging (test/db/firo_cache_clear_test.dart) timed out in that run and passes on its own. New tests cover slatepack routing, slate inspection, the QR limit, text normalisation, the import and finalize flows, note persistence, the Epicbox probe, on-chain notes, the Epicbox config model and the send address fields.Known issue, fixed separately:
fix/fee-estimate-abort.