tfpdev
3.1Part 3 · Confirmations

Confirmations

A client pays in digital dollars. One way of checking says 'paid' the second the payment shows up. The other waits a minute and keeps checking. Then make the payment disappear and see who already handed over the work.

01Try it

One invoice, one real payment, two ways of checking it. Pay, then make the payment vanish and see which checker already handed over the work.

Careful waits
Simulator
A real payment on a test network, in test money that is worth nothing. The vanishing is simulated, only on your screen: the real network won't do it on request.15 of 15 payments left this hour
Newest last—

No payment yet.

BreaksSays 'paid' right away
Sees the payment in a block, marks the invoice paid, done.
—Nothing paid yet.
HoldsWaits, then keeps checking
Waits for blocks to pile on top, remembers which block it trusted, checks it's still there.
—Nothing paid yet.

Pay the invoice. Both checkers watch the same payment. Then make it vanish and see which one already handed over the work.

15 payments an hour each. Every payment is real and uses up the lab's test money.

02Step 2

Why waiting wins

Showing up isn't the end

The newest part of the chain can still be swapped out. A payment in a swapped-out piece just isn't there anymore. Every new block on top makes that less likely.

Write down what you checked

The careful checker writes down which block it saw the payment in. Later it just checks that block is still there. If it isn't, it knows right away.

How long to wait is a money question

Waiting longer is safer but slower. A coffee can go out right away. A big invoice should wait.

03Step 3

The code

What runs behind the buttons, in one sentence. The code itself is one click away.

The bad version says 'paid' as soon as the payment is in a block and never looks again. The good version waits for more blocks, writes down which block it trusted, and checks that block every time it looks.

Show the code anyway
BreaksBreaks
The version that loses money. Read the grey notes under it, not the code.
watch-careless.tsts
const receipt = await client.getTransactionReceipt({ hash });if (receipt.status === "success") {  await markPaid(invoice);   // work goes out now}
A receipt means the transfer is in a block. It doesn't mean the block will stay. This code never asks again.
HoldsHolds
The version running behind the buttons above.
watch-careful.tsts
const tip = await client.getBlockNumber();const depth = tip - receipt.blockNumber + 1n;if (!deposit.creditedHash && depth >= N) {  await credit(invoice, receipt.blockHash);}// every time the watcher looks, even after creditingconst now = await client.getBlock({ blockNumber: receipt.blockNumber });if (deposit.creditedHash && now.hash !== deposit.creditedHash) {  await reverse(invoice);    // the chain changed under us}
Credit only with N blocks on top, store the block hash you trusted, and on every look check that block is still the one at that height.
04Step 4

Still curious?

A few more questions people ask. Open what you need.

Is the payment real?

Yes, on a test network where the money is worth nothing. The link next to the block opens the payment on a public explorer.

Then what is pretend?

Only the vanishing. The real network won't do it on request, so the button swaps the blocks on your screen. The checkers can't tell, and the careful one notices the same way it would in real life.

Does this happen for real?

Short swaps happen now and then on the main network, and deeper ones are rare. Other networks swap more often. After a few minutes, a block is effectively permanent.

Why did the careful one say 'paid' and then take it back?

Because you told it to wait fewer blocks than you then removed. Waiting makes this unlikely, not impossible, so it keeps checking even after saying 'paid'.

Not sure this could happen to you?

That's what the call is for. Tell me how your product works, and I'll tell you what applies to you and what I'd check first.