SIGN IN SIGN UP

fix: the pool takes its fee OUT of a deposit, not on top of it

An independent audit decoded the account's seven mainnet transactions and
settled the direction: send 20 to the pool, get 14 credited; unshield 5, pay
11. The ledger only balances that way (20-6, -5-6, +5-6, +5-6, +20-6, -5-6,
+5-6 = 3).

The widget had it backwards, so the shield-then-pay path was short by exactly
one fee at every invoice size: it deposited amount + fee, which credits the
amount, and then needed amount + fee. It could not work on mainnet, and the
refusal message sent the payer to deposit a figure that lands them back at the
same message forever. MockWallet modelled the chain backwards too, which is
why a test named "deposits enough to afford the payment that follows" passed.

- depositNeededFor / shieldedNeededFor: the two different questions, named.
- The confirm box quotes the fee twice when a deposit leg is coming, and
  recomputes once the wallet reveals a balance. It understated a 5 STRK
  invoice by 55%.
- The arcade starts with shielded funds, which is the flow the product
  recommends, and no longer breaks on the second coin.

Also, from the same audit:

- The receipt claimed "Proves this invoice was paid. Does not link the payment
  to the payer's wallet" - directly above the public deposit hash that names
  the payer, with the honesty panel hidden at that exact moment. It now states
  what it records, who confirms, and that a deposit made this close to the
  payment can be correlated. The panel stays.
- The honesty panel gained the RPC operator seeing the payer's IP, the limits
  on address freshness this page cannot verify, and the note-mode warning that
  a merchant may never see the payment.
- A new deliveryId per event: an underpaid invoice topped up later sent
  payment.confirmed under the id its payment.underpaid had used, and every
  merchant deduping as documented discarded the confirmation.
- Payments are counted from transfers into the address, not the balance delta,
  so sweeping the address no longer erases the payer's credit.
- localStorage, not sessionStorage: a second tab had no record and paid again.
  mountCheckout takes a store, which it never could before.
- A pending marker is written before the wallet is asked to move anything, so
  a wallet that submits and never returns cannot be paid twice on reload.
- Idempotency-Key is bound to the request, survives a restart, and refuses a
  concurrent duplicate.
- The token label is checked against the token address.
- Invoices without a deadline can be cancelled instead of being immortal.
- csvCell quotes a lone carriage return, which smuggled a live formula past
  the leading-character guard from a third-party RPC's tx hash.

102 tests, including fixtures rebuilt against the real economics.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
B
bongbongcrypto committed
c8ccdf69c31a0bf8127b17817532994f750de4d2
Parent: 764ae9e