Files
pezkuwi-wallet-android/feature-assets/src/main/res/layout/fragment_bridge_execution.xml
T
pezkuwichain 52e5645409 Split Bridge into input and dedicated execution screens, matching Swap
The single-screen design (amount input + live balance validation + submit
+ destination-wait, all in one ViewModel) was structurally fragile: the
live origin-balance observer and the amount-vs-balance validation ran in
the same reactive scope as execution, so a real successful transfer could
flash a false "insufficient balance" error the instant the balance dropped
post-submission. Patching that race (clearing the field, then an isExecuting
flag) kept fixing symptoms without addressing why the class of bug existed:
the app's own Swap flow avoids it entirely by keeping amount entry and
execution as separate screens/ViewModels, so validation code for the input
screen simply doesn't exist on the execution screen.

BridgeExecutionViewModel/Fragment now own the actual submit + destination-
wait, reached by navigating with a BridgeExecutionPayload once
BridgeViewModel.swapClicked() re-validates amount against the latest known
balance/reserve - a real confirmation gate, not just a UI button-disabled
look the domain layer never itself checked. A single BridgeExecutionState
sealed class (SubmittingOrigin/OriginFailed/WaitingForDestination/
DestinationConfirmed/DestinationPendingReview) replaces what used to be six
independently-updated LiveData flags that had to be kept in sync by hand.

Also fixed two real bugs found via live device testing with real funds:
- BridgeMultisigConstants.POLKADOT_USDT_ASSET_ID was 1 (the wallet's own
  chains.json ordinal for USDT on Polkadot Asset Hub) instead of 1984 (the
  real on-chain Assets pallet id, confirmed directly against the chain).
  getPolkadotUsdtReserve() queried asset 1, which the multisig has never
  held anything at, so the wUSDT->USDT reserve check always reported 0
  USDT available regardless of the multisig's real (and growing) holdings.
- The deposit-wait label's text was static XML, never updated on the
  success path - a genuinely completed bridge (destination balance
  confirmed, checkmark shown) still read "Waiting for confirmation..."
  forever, indistinguishable from actually being stuck. The new execution
  screen's label is driven by BridgeExecutionState instead.
2026-07-15 16:12:03 -07:00

61 lines
2.5 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/secondary_screen_background">
<io.novafoundation.nova.common.view.Toolbar
android:id="@+id/bridgeExecutionToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:titleText="@string/bridge_title" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:gravity="center"
android:orientation="vertical"
android:padding="24dp"
app:layout_constraintBottom_toTopOf="@id/bridgeExecutionDoneButton"
app:layout_constraintTop_toBottomOf="@id/bridgeExecutionToolbar">
<TextView
android:id="@+id/bridgeExecutionLabel"
style="@style/TextAppearance.NovaFoundation.Regular.Footnote"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/text_secondary"
tools:text="Waiting for confirmation on Pezkuwi Asset Hub…" />
<io.novafoundation.nova.common.view.ExecutionTimerView
android:id="@+id/bridgeExecutionTimer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp" />
<io.novafoundation.nova.common.view.AlertView
android:id="@+id/bridgeExecutionPendingReviewAlert"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:visibility="gone"
app:alertMode="warning" />
</LinearLayout>
<io.novafoundation.nova.common.view.PrimaryButton
android:id="@+id/bridgeExecutionDoneButton"
style="@style/Widget.Nova.Button.Primary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/common_done"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>