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.
State reads landed in 6b0e02e, but only for entries needing no key. This
needs the other half: enumerating a storage map.
Scope
Core — enumerate a map from its prefix. Runtime::storage_prefix(pallet, item) for the 32-byte prefix, and decode each value against the entry's
declared value type. Generic, not specific to this pallet.
Data — state_getKeysPaged to list keys under a prefix, and a batched
read for their values.
API — /v1/chains/:chain/reversible: each pending transfer with from, to, amount, tx_id, execute_at, and blocks remaining.
Web — /:chain/reversible, a countdown per row, linked from the section
nav.
What the page will say today
Nothing, and correctly. ReversibleTransfers has 0 of 6 calls dispatched on
mainnet and none of its 7 events has fired, so PendingTransfers is empty. The
page 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
DefaultDelay is BlockNumber(7200) and MinDelayPeriodBlocks is 2, so a
countdown 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_at is a DispatchTime<BlockNumber, Moment> — an enum, so it may be
either a block or a timestamp. Do not assume the block arm.
A wrong storage hasher reads as absent, not as an error (CLAUDE.md). The
prefix and hashers must come from the metadata, and the enumeration wants a
test pinned against real keys the same way storage_key has.
HighSecurityAccounts and its guardians are a natural follow-up on the same
machinery, but out of scope here.
## 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:
```
PendingTransfers H256 → PendingTransfer<AccountId32, u128, u32> [optional]
PendingTransfersBySender AccountId32 → BoundedVec<H256> [default]
HighSecurityAccounts AccountId32 → HighSecurityAccountData<…> [optional]
NextTransactionId u64 [default]
```
State reads landed in 6b0e02e, but only for entries needing **no key**. This
needs the other half: enumerating a storage map.
## Scope
1. **Core** — enumerate a map from its prefix. `Runtime::storage_prefix(pallet,
item)` for the 32-byte prefix, and decode each value against the entry's
declared value type. Generic, not specific to this pallet.
2. **Data** — `state_getKeysPaged` to list keys under a prefix, and a batched
read for their values.
3. **API** — `/v1/chains/:chain/reversible`: each pending transfer with `from`,
`to`, `amount`, `tx_id`, `execute_at`, and blocks remaining.
4. **Web** — `/:chain/reversible`, a countdown per row, linked from the section
nav.
## What the page will say today
Nothing, and correctly. `ReversibleTransfers` has **0 of 6 calls dispatched** on
mainnet and none of its 7 events has fired, so `PendingTransfers` is empty. The
page 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
- `DefaultDelay` is `BlockNumber(7200)` and `MinDelayPeriodBlocks` is 2, so a
countdown 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_at` is a `DispatchTime<BlockNumber, Moment>` — an enum, so it may be
either a block or a timestamp. Do not assume the block arm.
- A wrong storage hasher reads as **absent, not as an error** (CLAUDE.md). The
prefix and hashers must come from the metadata, and the enumeration wants a
test pinned against real keys the same way `storage_key` has.
- `HighSecurityAccounts` and its guardians are a natural follow-up on the same
machinery, but out of scope here.
The execution time lives with the Scheduler, not with the pending transfer. ReversibleTransfers::TransactionScheduled carries execute_at as an event
field, and nothing in PendingTransfers does.
So the countdown is a join of both halves, which is a better shape than
either alone:
State — PendingTransfers — is authoritative about what is still
pending. A transfer that was cancelled or executed is gone from the map, and
no amount of event replay is as trustworthy as its absence.
Events — TransactionScheduled, keyed by tx_id — supply when it is due
and 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 DispatchTime note above still stands but applies to the
event field rather than to a storage value.
Correction to the scope above, found while reading the pallet.
**`PendingTransfer` does not carry `execute_at`.** The stored struct is:
```rust
pub struct PendingTransfer<AccountId, Balance, AssetId> {
pub from: AccountId,
pub to: AccountId,
pub guardian: AccountId,
pub asset_id: Option<AssetId>,
pub amount: Balance,
}
```
The execution time lives with the `Scheduler`, not with the pending transfer.
`ReversibleTransfers::TransactionScheduled` carries `execute_at` as an event
field, and nothing in `PendingTransfers` does.
So the countdown is a **join of both halves**, which is a better shape than
either alone:
- **State** — `PendingTransfers` — is authoritative about *what is still
pending*. A transfer that was cancelled or executed is gone from the map, and
no amount of event replay is as trustworthy as its absence.
- **Events** — `TransactionScheduled`, keyed by `tx_id` — supply *when it is due*
and 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 `DispatchTime` note above still stands but applies to the
event field rather than to a storage value.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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.