Skip to content

feat(transaction-pay-controller): add Money Account vault deposit and withdraw actions - #9849

Draft
saustrie-consensys wants to merge 3 commits into
mainfrom
saustrie/vba-money-account-vault-functions
Draft

feat(transaction-pay-controller): add Money Account vault deposit and withdraw actions#9849
saustrie-consensys wants to merge 3 commits into
mainfrom
saustrie/vba-money-account-vault-functions

Conversation

@saustrie-consensys

@saustrie-consensys saustrie-consensys commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Explanation

Money Account holds mUSD on Monad, and only vault shares (vmUSD) earn yield and are usable for Card spend. Today mUSD that lands on a Money Account from outside a user-initiated MM Pay flow (for example a MoonPay/Iron payout) is only vaulted by CHOMP's delayed auto-sweep, and there is no reusable way to redeem vmUSD and send the resulting mUSD to a partner deposit address in one shot. This PR adds both directions to TransactionPayController as messenger actions so clients can drive them.

TransactionPayController:submitMoneyAccountVaultDeposit

Headless entrypoint for vaulting a completed payout. The caller passes the Money Account address and the payout transaction hash; the controller resolves the actual mUSD amount and block from chain via the existing getTransferredAmountFromTxHash helper rather than trusting a caller-supplied amount, builds the approve/deposit pair with the canonical @metamask/money-account-utils builder, and submits it as an internal sponsored batch with requireApproval: false. It reuses the CHOMP race guard that already exists for the MM Pay deposit path, so if CHOMP sweeps the same funds first the batch is skipped instead of double-depositing.

To make this reusable, the existing submitMoneyAccountVaultDeposit util was split: the batch submission plus CHOMP pre/post checks now live in submitMoneyAccountVaultDepositBatch, which works with or without a parent TransactionMeta, and the original function is a thin wrapper over it.

Concurrent calls for the same payout hash share one in-flight submission. Successful vault results are retained in an in-memory Map for the controller lifetime so retries return the prior hash without submitting again. Skipped results ({ skipped: true } when vaulting is disabled) are not retained, so enabling the feature flag later can retry the same payout hash without restarting the app. This is not durable across process restarts.

TransactionPayController:submitMoneyAccountVaultWithdraw

User-confirmed exact-out redemption. The request is slim: { amountInRaw, moneyAccountAddress, recipient, requestId }. The controller caps the amount at the withdrawable vmUSD-equivalent balance reported by MoneyAccountBalanceService, builds the withdraw and transfer calls with buildMoneyAccountWithdrawBatch, and submits them as a single atomic: true batch with requireApproval: true. Atomicity matters here: if the redeem and the transfer were separate, CHOMP could re-vault the idle mUSD in between and the transfer would fail. Quote expiry, chain, token, and Iron quote field validation are intentionally out of scope for this action; callers keep those concerns in Ramps / NeoBank layers.

Other notes

  • Both actions read vault addresses from moneyAccountVaultConfig in RemoteFeatureFlagController and are individually gated by deposit/withdraw enablement flags. A disabled deposit returns { skipped: true }; a disabled withdraw throws.
  • Concurrent calls are deduplicated (by transaction hash and by request ID respectively). Deposit success caching is controller-lifetime in-memory only, as described above.
  • @metamask/money-account-utils was added as a dependency of transaction-pay-controller (with the matching tsconfig project references) so the calldata builders stay canonical instead of being reimplemented here.

References

Consumer PR: MetaMask/metamask-mobile#34700

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

… withdraw actions

Money Account mUSD that arrives from an external payout (MoonPay/Iron) can
only be vaulted by CHOMP's delayed auto-sweep, and there is no reusable path
for redeeming vmUSD straight to a partner deposit address. Add two messenger
actions so the client can drive both directions itself.
@saustrie-consensys
saustrie-consensys force-pushed the saustrie/vba-money-account-vault-functions branch from 7703a9c to 6964382 Compare August 12, 2026 17:37
…ithdraw

Persist successful deposit/withdraw results for process-lifetime idempotency,
tighten CHOMP matching to exact vault destination and amount, slim the withdraw
request surface, and return { skipped: true } instead of a fake 0x hash when
vaulting is disabled.
Retain successful deposit results for controller-lifetime dedupe, but clear
skipped results so enabling vaulting later can retry the same payout hash.
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