Files
wallet/ui
rob thijssen 95bcfe9ca4
Some checks are pending
ci / gate (push) Waiting to run
feat(substrate): transfers prepared for review, priced, signed by the session, submitted and watched
`SubstrateAdapter` implements the chain-adapter port for Quantus. `prepare`
builds the call from a chain-neutral intent, decodes it back from its own
bytes through the runtime metadata into a summary a person can check (a
32-byte account renders as the chain's SS58 address, an amount in the
token's units), prices it by assembling a full extrinsic with a zeroed
signature of the right size and asking `TransactionPaymentApi_query_info`,
and returns the signer payload for the session to sign. `submit` refuses
if the runtime spec moved or the payload no longer matches, assembles with
the session's signature, and streams statuses; a dispatch error is reported
at inclusion, named `Pallet::Variant` where the chain names it, so nobody
waits for finality to learn a transfer failed.

Reversible transfers: `reversible_for` routes through
`ReversibleTransfers::schedule_transfer_with_delay`; a high-security
account is routed through the pallet's default-delay call whether it asked
or not, since the chain's extension refuses a plain transfer from one.
`pending_reversible` lists what is waiting with the execution block from
the scheduler's lookup, whose task name is the tx id itself, and
`CancelReversible` cancels.

Three things the dev node taught, each now written into the code:

- Mortality must be anchored at the *best* block. Finality on this
  proof-of-work chain trails the tip by a hundred blocks, and an era born
  at the finalized head names a birth block whose hash the runtime resolves
  differently: "Transaction has a bad signature", for both schemes, while
  immortal transactions from the same keys passed. Probed all four
  scheme-by-era combinations to isolate it.
- Everything the UI shows is read at the best block, with the block number
  and the finalized height alongside so confirmations can be shown.
  subxt's `at_latest()` is the finalized block; the pending list read there
  showed the state of a hundred blocks ago while the chain's events showed
  every schedule landing.
- subxt's runtime-update subscription died on this node with "header not
  found" after a reorg and took the connection with it. It is advisory
  now: on error the runtime version is polled every ten heads and a change
  forces a reconnect, which refetches metadata.

Live, against `quantus-node --dev` with the funded dev accounts: a 3 DEV
transfer from crystal_alice to crystal_bob is included and moves bob's
balance; a reversible transfer with a 200-block window is scheduled, shows
as pending with its execution block, and is cancelled; an overspend fails
as `Token(FundsUnavailable)`. Fee for a plain transfer: 0.010157927 DEV.

In the app: `transfer_prepare` (amount parsed from its decimal form in
Rust), `transfer_submit` (signs through the session, emits `tx:status`),
`transfer_discard`, `reversible_pending`, `reversible_cancel_prepare`.

Closes #22

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014ftBXYuba8ARhQeF74oUgW
2026-09-15 22:58:39 +03:00
..