pending reversible transfers, with a cancel that works until the block it executes #29

Closed
opened 2026-09-15 12:39:40 +00:00 by grenade · 1 comment
Owner

Parent: #4

Scope

A list of PendingTransfersBySender for each account with the execute-at block, a countdown derived from the chain's measured block interval (not a hard-coded 12 s), and a cancel action that goes through the send review pattern. Executed and cancelled transfers move to history.

Acceptance

Cancel within the window returns the funds; the countdown is within one block of when execution actually happens on Heisenberg.

Depends on

#22 (transfer, including cancel), the send screen.

Parent: #4 ## Scope A list of `PendingTransfersBySender` for each account with the execute-at block, a countdown derived from the chain's measured block interval (not a hard-coded 12 s), and a cancel action that goes through the send review pattern. Executed and cancelled transfers move to history. ## Acceptance Cancel within the window returns the funds; the countdown is within one block of when execution actually happens on Heisenberg. ## Depends on #22 (transfer, including cancel), the send screen.
grenade added the child label 2026-09-15 12:41:37 +00:00
Author
Owner

The countdown is built as specified: blocks remaining times the interval measured over the last 32 best heads, never a hard-coded figure. Measured on the dev node.

window 60 blocks, listed with 49 remaining
measured interval at the time 1.1 s
estimate 52 s
actual arrival of the execute block 65.6 s
error 13.7 s, about 12 blocks at the measured pace

The dev node's pace over that minute swung between 0.6 and 1.7 s per ten blocks. That is not a dev-node quirk so much as proof of work: block arrivals are close to Poisson, so the error of a countdown over N blocks grows like the square root of N times the mean interval. Over 49 blocks that is about 7 blocks of one standard deviation on any PoW chain, Heisenberg included. "Within one block" is therefore only achievable for the final block or two, not for the whole window.

What the UI does about it: the figure is labelled "about" when measured and "nominally" when it is the profile's block time, and the block number itself is shown alongside, which is exact. The check against Heisenberg is still owed once #53 gives the wallet a public testnet endpoint; the criterion there should be read as "the estimate converges on the actual time as the window closes", which is what a per-head refetch gives.

One more thing surfaced on the way: a cancel's review showed the transaction id as an SS58 address, because the decoder treated any 32 bytes as an account. It now decides by the argument's type in metadata; a unit test pins it.

The countdown is built as specified: blocks remaining times the interval measured over the last 32 best heads, never a hard-coded figure. Measured on the dev node. | | | |---|---| | window | 60 blocks, listed with 49 remaining | | measured interval at the time | 1.1 s | | estimate | 52 s | | actual arrival of the execute block | 65.6 s | | error | 13.7 s, about 12 blocks at the measured pace | The dev node's pace over that minute swung between 0.6 and 1.7 s per ten blocks. That is not a dev-node quirk so much as proof of work: block arrivals are close to Poisson, so the error of a countdown over N blocks grows like the square root of N times the mean interval. Over 49 blocks that is about 7 blocks of one standard deviation on any PoW chain, Heisenberg included. "Within one block" is therefore only achievable for the final block or two, not for the whole window. What the UI does about it: the figure is labelled "about" when measured and "nominally" when it is the profile's block time, and the block number itself is shown alongside, which is exact. The check against Heisenberg is still owed once #53 gives the wallet a public testnet endpoint; the criterion there should be read as "the estimate converges on the actual time as the window closes", which is what a per-head refetch gives. One more thing surfaced on the way: a cancel's review showed the transaction id as an SS58 address, because the decoder treated any 32 bytes as an account. It now decides by the argument's type in metadata; a unit test pins it.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: blackbeard/wallet#29