Skip to content

Add makeMaxSpend wallet API with core fallback shim#727

Open
j0ntz wants to merge 1 commit into
masterfrom
jon/make-max-spend
Open

Add makeMaxSpend wallet API with core fallback shim#727
j0ntz wants to merge 1 commit into
masterfrom
jon/make-max-spend

Conversation

@j0ntz

@j0ntz j0ntz commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Description

Add a makeMaxSpend method to EdgeCurrencyWallet and EdgeMemoryWallet. It has the same signature as makeSpend but builds a transaction that atomically spends the maximum amount, avoiding the race that affects separate getMaxSpendable + makeSpend calls when network state changes between them.

The wallet API routes to EdgeCurrencyEngine.makeMaxSpend when the engine implements it. For engines that don't, the core provides a fallback shim that combines getMaxSpendable and makeSpend (so every wallet gets makeMaxSpend regardless of engine support). EdgeCurrencyEngine.makeMaxSpend is therefore optional.

getMaxSpendable is now deprecated on the public EdgeCurrencyWallet and EdgeMemoryWallet APIs. The engine-level getMaxSpendable remains (the fallback shim still uses it as a building block).

makeSpend's implementation was extracted into a shared makeSpendInner helper that both makeSpend and makeMaxSpend delegate to. For a max spend the saved spend targets are backfilled from the resulting transaction so the recorded amount reflects the real send.

Asana: https://app.asana.com/0/1215088146871429/1207967192999590

Testing

  • npm run types (tsc) clean.
  • npm test (mocha) — 163 passing, including a new can make max spend test that exercises the core fallback shim end to end.
  • Exercised live in the running app (iOS sim): tapping "Max" on a Send screen invokes coreWallet.makeMaxSpend, which built the max-spend transaction (max amount = balance − network fee) and rendered it ready to send.

Note

Medium Risk
Changes the wallet spend construction path and max-send behavior; fallback still uses the legacy two-step max calculation, while native engine support affects fee/amount correctness for max sends.

Overview
Adds makeMaxSpend on EdgeCurrencyWallet and EdgeMemoryWallet (same shape as makeSpend) so clients can build a max-amount send in one call instead of getMaxSpendable then makeSpend, which can race when balances or fees change between steps.

makeSpend logic is consolidated into makeSpendInner in currency-wallet-api.ts, used by both makeSpend and makeMaxSpend. When the engine implements optional EdgeCurrencyEngine.makeMaxSpend, the core delegates there; otherwise it falls back to getMaxSpendableInner plus makeSpend on the first spend target. For max spends, saved spendTargets metadata is backfilled from the built transaction (native amount minus network fee for parent currency).

Public getMaxSpendable on currency and memory wallets is deprecated in favor of makeMaxSpend; engine getMaxSpendable remains for the shim. A can make max spend test covers the fallback path on the fake engine.

Reviewed by Cursor Bugbot for commit 40b8361. Bugbot is set up for automated code reviews on this repo. Configure here.

Add makeMaxSpend to EdgeCurrencyWallet and EdgeMemoryWallet, mirroring
makeSpend but building a transaction that atomically spends the maximum
amount. The wallet API routes to EdgeCurrencyEngine.makeMaxSpend when the
engine implements it, otherwise falls back to a shim that combines
getMaxSpendable and makeSpend. Deprecate the public getMaxSpendable API.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 40b8361. Configure here.

return await makeSpendInner(
spendInfo,
async (engineSpendInfo, opts) =>
await makeMaxSpend(engineSpendInfo, opts),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unbound makeMaxSpend breaks class engines

High Severity

When a native EdgeCurrencyEngine.makeMaxSpend exists, the wallet copies it off the engine and invokes the bare function. Class-based engines (the usual pattern here) rely on this; that call drops the engine instance and can throw or misbehave at runtime.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 40b8361. Configure here.

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.

1 participant