Surface pending reversible transfers, with a live countdown #1
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
A page showing money that is currently in flight and still cancellable on
ReversibleTransfers, counting down to the block it executes at.No other explorer can have this, because no other chain has the pallet. It is
the single most distinctive thing Quantus does and today the observer says
nothing about it.
Why it needs state reads, not the event index
The event index can see a transfer being scheduled, cancelled or executed
after the fact. It cannot answer "what is pending right now" without replaying
every event since genesis and reconstructing the set — and getting the
reconstruction wrong is silent.
The chain already holds the answer:
State reads landed in
6b0e02e, but only for entries needing no key. Thisneeds the other half: enumerating a storage map.
Scope
Runtime::storage_prefix(pallet, item)for the 32-byte prefix, and decode each value against the entry'sdeclared value type. Generic, not specific to this pallet.
state_getKeysPagedto list keys under a prefix, and a batchedread for their values.
/v1/chains/:chain/reversible: each pending transfer withfrom,to,amount,tx_id,execute_at, and blocks remaining./:chain/reversible, a countdown per row, linked from the sectionnav.
What the page will say today
Nothing, and correctly.
ReversibleTransfershas 0 of 6 calls dispatched onmainnet and none of its 7 events has fired, so
PendingTransfersis empty. Thepage has to read as "nothing is in flight" rather than as an error or a blank —
and it has to be there before the first one, because catching the first
scheduled transfer is the point.
Notes for whoever picks this up
DefaultDelayisBlockNumber(7200)andMinDelayPeriodBlocksis 2, so acountdown can span anything from seconds to a day. Show blocks and an
estimate in time, and say which is which — block count is the fact, wall
time is derived from a measured interval that moves.
execute_atis aDispatchTime<BlockNumber, Moment>— an enum, so it may beeither a block or a timestamp. Do not assume the block arm.
prefix and hashers must come from the metadata, and the enumeration wants a
test pinned against real keys the same way
storage_keyhas.HighSecurityAccountsand its guardians are a natural follow-up on the samemachinery, but out of scope here.
Correction to the scope above, found while reading the pallet.
PendingTransferdoes not carryexecute_at. The stored struct is:The execution time lives with the
Scheduler, not with the pending transfer.ReversibleTransfers::TransactionScheduledcarriesexecute_atas an eventfield, and nothing in
PendingTransfersdoes.So the countdown is a join of both halves, which is a better shape than
either alone:
PendingTransfers— is authoritative about what is stillpending. A transfer that was cancelled or executed is gone from the map, and
no amount of event replay is as trustworthy as its absence.
TransactionScheduled, keyed bytx_id— supply when it is dueand when it was scheduled.
A row that appears in state with no matching event is possible (the event index
reaches back only as far as it has read) and must render as pending with an
unknown deadline rather than being dropped.
This also means the
DispatchTimenote above still stands but applies to theevent field rather than to a storage value.