Skip to content

Richer send confirmation: resolved recipient, balance impact, simulation - #43

Merged
portdeveloper merged 3 commits into
portdeveloper:mainfrom
ColinkaMir:feat/richer-send-confirm
Aug 12, 2026
Merged

Richer send confirmation: resolved recipient, balance impact, simulation#43
portdeveloper merged 3 commits into
portdeveloper:mainfrom
ColinkaMir:feat/richer-send-confirm

Conversation

@ColinkaMir

Copy link
Copy Markdown
Contributor

What this changes

Closes #24. Sends now render a resolved confirmation block before the y/N prompt: checksummed recipient, amount + wallet balance after the send, gas expectation (gasless / dry-run / native), and a simulated outcome so obvious reverts surface before confirmation.

  • format.mjs: toChecksumAddress (EIP-55 via ethers getAddress) — a typo'd mixed-case checksum throws, so it is refused before the prompt.
  • tools.mjs: previewSend (checksum + balance read + fee quote + post-send balance, best-effort simulation) and renderSendPreview. A failed simulation is rendered as a revert warning instead of being swallowed; insufficient balance is a warning line, so dry-run previews still render. runAction sends to the checksummed address.
  • cli.mjs: send_mon builds the preview first and refuses pre-prompt on failure (scripted mode exits non-zero); anything but yes still aborts.

Example (dry-run):

  To:       0x92936497B6ad2BA84b3f7Af22C9afF15f00b13B5
  Amount:   0.01 MON
  Gas:      dry-run (simulated, nothing broadcast)
  Balance:  0.0 -> -0.01 MON
  WARNING:  balance is below the amount — this send would revert.
  confirm? [y/N]

Acceptance: both dry-run (no PIMLICO_API_KEY) and gasless mode show the block (the block is built in handleAction before the gas-mode branch, and previewSend labels each mode); npm run smoke prints SMOKE_OK; a typo'd checksum (0x92936497b6ad2BA84... — first letter lowercased) is rejected before the confirmation prompt with exit 1 in scripted mode.

Rebased on current main (includes #25 ERC-20 balance reads); npm test 33/33 after the rebase.

How I tested it

  • npm run build succeeds
  • npm run smoke prints SMOKE_OK
  • Ran the REPL and exercised the change
  • Dry-run send
  • Real gasless send (paste the tx hash if so):

Model / platform tested on: QWEN3_8B_INST_Q4_K_M · Ubuntu 22.04 headless VPS (lavapipe/llvmpipe Vulkan), Node 20

Scope check

  • This stays within v0 scope (native MON, single account, testnet), OR
  • This grows the scope, and I've described the new surface above.

Conventions

  • I used npm (not pnpm/yarn) and did not add a global sodium-native override.
  • I did not commit .env, seeds, keys, or model weights.

Before a send executes, show a resolved confirmation block instead of the
one-line preview:

- recipient resolved to its EIP-55 checksummed form; a mixed-case address
  whose checksum does not verify is a typo and is rejected *before* the
  confirmation prompt (format.toChecksumAddress via ethers getAddress)
- amount and token, plus the wallet balance before -> after the send
- gas expectation labelled by mode (gasless via paymaster / dry-run
  simulated / you-pay-gas), with the simulated fee where a quote is available
- an insufficient balance is surfaced as a WARNING line in the block (the
  send would revert) rather than blocking dry-run previews
- the existing y/N confirm and the mainnet acknowledgement are unchanged;
  runAction now sends to the checksummed address so it matches the preview

New in tools.mjs: previewSend() (async pre-flight, throws on bad checksum)
and renderSendPreview(). cli.handleAction builds the preview for send_mon
and rejects before the prompt on failure.

Tested on Linux (lavapipe Vulkan): dry-run and gasless both render the
block; a typo'd-checksum send is refused before the prompt (exit 1, no
confirm shown); interactive pty send shows the block then the y/N prompt;
npm run smoke SMOKE_OK.
The confirmation block already flagged an insufficient balance; a
quoteSend that throws is itself an obvious revert the simulator caught,
so show it as a WARNING line before the prompt instead of swallowing it.
Keeps the block informative when the estimate path fails, and matches the
issue's 'obvious reverts are caught before confirmation'.
@ColinkaMir

Copy link
Copy Markdown
Contributor Author

Rebased onto main after #47 landed; conflicts were import lines only, resolved. Re-verified on the rebased build: npm test 72/72 (including the new send-token and fetch-model suites), npm run smoke SMOKE_OK, and the acceptance checks (dry-run preview block, typo'd checksum refused pre-prompt with exit 1, non-yes cancels). Note: the new send_token action currently keeps the one-line preview; extending the resolved preview block to token sends looks like natural follow-up work once this lands.

@ColinkaMir
ColinkaMir force-pushed the feat/richer-send-confirm branch from b958c1f to 01bde08 Compare August 8, 2026 12:36

@portdeveloper portdeveloper left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read the full diff at 939d9da plus 01bde08 and checked the rebase against current main (the symbols the diff leans on all exist there: println, hadFailure, wallet.quoteSend, wallet.getBalance, and isWrite now covering send_token). Standing alone the change is good. previewSend in src/tools.mjs throws on a bad mixed-case checksum before the prompt, a failed quoteSend and an insufficient balance both surface as WARNING lines in the block, and scripted mode exits non-zero on a refused preview, which matches the acceptance notes on #24.

I'm requesting changes on one structural point: how this composes with #42, which is approved and will land. Two things break.

previewSend resolves the recipient itself, straight from the raw action.to via toChecksumAddress. Once #42 lands, action.to can be an address-book name like "alice", and getAddress("alice") throws, so every alias send would be refused before the prompt. #42's handleAction resolves once via resolveSend and hands the result down; previewSend needs to take that already-resolved address as input rather than resolving on its own.

handleAction also throws away the address the preview computed: runAction's send_mon case re-derives it with its own toChecksumAddress(a.to) call. Today that is harmless because checksumming an in-memory string is deterministic, but the send path never actually uses the previewed value, and #42's runAction takes the resolved recipient as an argument precisely so nothing re-resolves between the y/N prompt and the signature. Pass preview.to into runAction so there is a single resolution point. With that shape the eventual #42 merge is close to mechanical: resolve once in handleAction, then feed that same address through the preview and the send.

Two smaller notes. The comment in src/cli.mjs handleAction says an over-balance send is "rejected before the confirmation prompt", but the code (correctly, per #24) only prints a WARNING and still prompts, so update the comment. And previewSend's insufficient check compares value > before without the fee, so a native-gas send that only reverts because of gas shows no warning; fine as a follow-up if you note it.

The rebase itself looks clean (import-line conflicts only, as you described), and keeping the one-line preview for send_token as follow-up work is fine with me.

… preview and send

Per the review on portdeveloper#43: previewSend now takes the already-resolved checksummed
recipient instead of resolving raw action.to itself, so when portdeveloper#42's address
book lands, name resolution slots in ahead of the checksum step and alias
sends stop dying on getAddress. handleAction resolves once, the preview and
runAction both receive that address, and nothing re-resolves between the y/N
prompt and the signature (direct runAction callers keep a fallback resolve).
Also: corrected the stale over-balance comment (it is a WARNING plus prompt,
not a pre-prompt rejection) and noted the fee-exclusive insufficient check as
a known follow-up.
@ColinkaMir

Copy link
Copy Markdown
Contributor Author

Done in 1a9274c, point by point:

  • Single resolution point. handleAction now resolves the recipient once and threads it through: previewSend(a, to) takes the already-resolved address (it no longer touches raw action.to), and runAction(a, resolvedTo) receives the same value, so nothing re-resolves between the y/N prompt and the signature. When Resolve recipient names to addresses before a send #42 lands, resolveSend slots in right before the checksum step in handleAction and everything downstream just works; the spot is marked with a comment.
  • Direct runAction callers (e2e, library use) keep a fallback resolve so the smoke path stays intact.
  • Stale comment fixed: over-balance is described as a WARNING plus prompt, per Richer send confirmation with resolved recipient, balance impact, and simulation #24.
  • Fee-exclusive insufficient check noted in the code as a known follow-up, as suggested.

Re-verified on the revised build: npm test 72/72, smoke SMOKE_OK, and the acceptance checks (preview block renders, typo'd checksum refused pre-prompt with exit 1, non-yes cancels).

@portdeveloper portdeveloper left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed the follow-up commit against current main and the requested changes are addressed: the recipient is checksummed once before preview, the exact previewed address is threaded into execution, the stale comment is corrected, and the fee-only insufficiency edge case is explicitly scoped as follow-up. CI is green; locally verified 72/72 tests, production build, and a disposable-wallet dry-run covering the rendered balance warning, dry-run execution, and invalid mixed-case checksum refusal. Approved.

@portdeveloper
portdeveloper merged commit bf6affc into portdeveloper:main Aug 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Richer send confirmation with resolved recipient, balance impact, and simulation

2 participants